Trabajo elaborado para la asignatura “Programación y manejo de datos en la era del Big Data” de la Universitat de València durante el curso 2021-2022. El repo del trabajo está aquí.

La página web de la asignatura y los trabajos de mis compañeros pueden verse aquí.


Librería de paquetes

Campeón del mundo

Historia de la F1

LOGOS DE LA FORMULA 1

Logo de 1985 a 1986

Logo de 1985 a 1986

Logo de 1987 a 1993

Logo de 1987 a 1993

Logo de 1994 a 2017

Logo de 1994 a 2017

Logo Actual

LOGO desde 2018 hasta la actualidad

Protagonistas

#---PREPARACION DE LOS DATOS

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
#str(resultados)
#str(pilotos)
#pilotos[, c(1)] <- sapply(pilotos[, c(1)], as.numeric)
#resultados[, c(3,6,9)] <- sapply(resultados[, c(3,6,9)], as.numeric)

#----------------------------------------------------------------------------------
#numero de carreras que ha corrido cada piloto

#explicacion de lo que hago, no se porque la variable sumatorio no la detecta como numerica aunque la pase a numerica, por tanto al ordenar con slice max no funciona, lo que he hecho es usar la funcion arrange, que ordena de menor a mayor, pero como queremos los que mas carreras han corrido, no me sirve de menor a mayor, por tanto he multiplicado la variable del sumatorio por -1, he usado arrange para que los que más carreras tienen salgan primero, y luego he vuelto a multiplicar por -1. luego he cogido mayores de 202 carreras, que son los 20 que más tienen, porque si hago slice se descuadra y te devuelve el df del principio

n_carreras <- resultados %>% group_by(driverId) %>% mutate(numero_carreras = sum(n())) %>% distinct(numero_carreras) %>% arrange(desc(numero_carreras)) #mutate(numero_carreras_final = numero_carreras*-1)

n_carreras_nom <- full_join(n_carreras, pilotos, c ("driverId" = "driverId")) %>% select(driverId, driverRef, numero_carreras)  %>%  filter(numero_carreras >= 202 ) #los 20 qque mas carreras tienen (no funciona usar slice_max)

#--------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# numero de victorias por piloto
victorias <- resultados %>% filter(position == "1") %>% group_by(driverId) %>% mutate(n_victorias = sum(n())) %>% distinct(n_victorias) %>% arrange(desc(n_victorias))#mutate(n_victorias_final = n_victorias*-1)

#aqui fusiono con el df de pilotos para que aparezca el nombre y no sólo el ID del piloto en cuestion, y hago lo mismo que en el apartado de arriba para ordenar
victorias_con_nombre <- full_join(victorias, pilotos, c ("driverId" = "driverId")) %>% select(driverId, nationality, driverRef, n_victorias)  #los 10 con mas victorias, tmp funciona slice_max
mas_victorias <- victorias_con_nombre %>%  filter(n_victorias >= 25 ) 




#-------------------------------------------------

#resultado medio
str(resultados)
#> 'data.frame':    25140 obs. of  18 variables:
#>  $ resultId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ raceId         : int  18 18 18 18 18 18 18 18 18 18 ...
#>  $ driverId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ constructorId  : int  1 2 3 4 1 3 5 6 2 7 ...
#>  $ number         : chr  "22" "3" "7" "5" ...
#>  $ grid           : int  1 5 7 11 3 13 17 15 2 18 ...
#>  $ position       : chr  "1" "2" "3" "4" ...
#>  $ positionText   : chr  "1" "2" "3" "4" ...
#>  $ positionOrder  : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ points         : num  10 8 6 5 4 3 2 1 0 0 ...
#>  $ laps           : int  58 58 58 58 58 57 55 53 47 43 ...
#>  $ time           : chr  "1:34:50.616" "+5.478" "+8.163" "+17.181" ...
#>  $ milliseconds   : chr  "5690616" "5696094" "5698779" "5707797" ...
#>  $ fastestLap     : chr  "39" "41" "41" "58" ...
#>  $ rank           : chr  "2" "3" "5" "7" ...
#>  $ fastestLapTime : chr  "1:27.452" "1:27.739" "1:28.090" "1:28.603" ...
#>  $ fastestLapSpeed: chr  "218.300" "217.586" "216.719" "215.464" ...
#>  $ statusId       : int  1 1 1 1 1 11 5 5 4 3 ...
resultados[, c(7)] <- sapply(resultados[, c(7)], as.numeric)
resultados[is.na(resultados)] <- 25 

resultado_medio <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, position) %>% group_by(driverId) %>% mutate(result_medio = mean(position)) %>% distinct (driverId, driverRef, result_medio) %>% arrange(result_medio)

#resultado medio en clasificacion
resultado_medio_clas <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, grid) %>% group_by(driverId) %>% mutate(result_medio_clas = mean(grid)) %>% distinct (driverId, driverRef, result_medio_clas)  %>% filter(result_medio_clas > 0) %>% arrange(result_medio_clas)  


#numero de vueltas liderando


#puntos por carrera (puntos/carrera)

puntos_x_carrera <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, points) %>% full_join(., n_carreras,  c ("driverId" = "driverId")) %>% group_by(driverId) %>% mutate(total_puntos = sum(points)) %>% distinct(driverId, driverRef, numero_carreras, total_puntos) %>% mutate(media_puntos = total_puntos/numero_carreras) %>% arrange(desc(media_puntos))

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])
#se necesita "mas_victorias"


gg_mas_victorias <- ggplot(mas_victorias, aes(x = reorder(driverRef, n_victorias), y = n_victorias )) + geom_bar(stat = "identity") + labs(x = "Piloto" , y = "Número de victorias")
gg_mas_victorias
#trabajo --> darle formato chulo, ya veremos este puente si le podemos meter dinámico o que

Parrilla de pilotos 2021

#fotos_pil_2021 <- c("./imagenes/pilotos/hamilton.png", "./imagenes/pilotos/bottas.png", "./imagenes/pilotos/verstappen.jng", "./imagenes/pilotos/perez.png", "./imagenes/pilotos/sainz.png", "./imagenes/pilotos/leclerc.png", "./imagenes/pilotos/norris.png", "./imagenes/pilotos/ricciardo.png", "./imagenes/pilotos/vettel.png", "./imagenes/pilotos/stroll.png", "./imagenes/pilotos/alonso.png", "./imagenes/pilotos/ocon.jng", "./imagenes/pilotos/gasly.png", "./imagenes/pilotos/tsunoda.png", "./imagenes/pilotos/russell.png", "./imagenes/pilotos/latifi.png", "./imagenes/pilotos/raikkonen.png", "./imagenes/pilotos/giovinazzi.jng", "./imagenes/pilotos/mick.png", "./imagenes/pilotos/mazepin.png")

#fotos_pais_2021 <- c("./imagenes/pilotos/hamilton.png", "./imagenes/pilotos/bottas.png", "./imagenes/pilotos/verstappen.jng", "./imagenes/pilotos/perez.png", "./imagenes/pilotos/sainz.png", "./imagenes/pilotos/leclerc.png", "./imagenes/pilotos/norris.png", "./imagenes/pilotos/ricciardo.png", "./imagenes/pilotos/vettel.png", "./imagenes/pilotos/stroll.png", "./imagenes/pilotos/alonso.png", "./imagenes/pilotos/ocon.jng", "./imagenes/pilotos/gasly.png", "./imagenes/pilotos/tsunoda.png", "./imagenes/pilotos/russell.png", "./imagenes/pilotos/latifi.png", "./imagenes/pilotos/raikkonen.png", "./imagenes/pilotos/giovinazzi.jng", "./imagenes/pilotos/mick.png", "./imagenes/pilotos/mazepin.png")

#fotos_esc_2021 <- c("./imagenes/escuderias/mercedes.png","./imagenes/escuderias/mercedes.png","./imagenes/escuderias/redbull.png", "./imagenes/escuderias/redbull.png","./imagenes/escuderias/ferrari.png", "./imagenes/escuderias/ferrari.png","./imagenes/escuderias/mclaren.png", "./imagenes/escuderias/mclaren.png","./imagenes/escuderias/aston.png", "./imagenes/escuderias/aston.png","./imagenes/escuderias/alpine.png", "./imagenes/escuderias/alpine.png","./imagenes/escuderias/alphatauri.png", "./imagenes/escuderias/alphatauri.png","./imagenes/escuderias/williams.png", "./imagenes/escuderias/williams.png","./imagenes/escuderias/alfaromeo.png", "./imagenes/escuderias/alfaromeo.png","./imagenes/escuderias/haas.png", "./imagenes/escuderias/haas.png",)


#library(gt)
#mundial_2021 <- datos_de_alex %>% gt() %>% text_transform( locations = cells_body(columns = c(fotos_nac_2021)), fn = function(x) {gt::local_image(x, height = 50)}) %>% text_transform( locations = cells_body(columns = c(fotos_pais_2021)), fn = function(x) {gt::local_image(x, height = 50)}) %>% text_transform( locations = cells_body(columns = c(fotos_esc_2021)), fn = function(x) {gt::local_image(x, height = 50)}) %>% tab_header(title = md("**Pilotos 2021**"), subtitle = md("Parrilla")) %>%   cols_label(
   # driverRef = html(""),
    #numero_carreras = html("Nº carreras"),
    #n_victorias = html("Nº victorias"),
    #fotos_esp_ing = html("País"),
    #fotos_ALO_vs_HAM = html("")) %>%  
  #tab_options(table.background.color = "gray13",   table.font.color.light = "cyan") %>% 
  #cols_align(align = "center",
  #columns = everything())

#alo_vs_ham_tabla

Españoles por la F1

#--------------------------------------------------------------------------------------
#pilotos españoles

pilotos <- rio::import(file = "./datos/drivers.csv")

pilotos_esp <- pilotos %>% filter(nationality == "Spanish") %>% select(driverId, driverRef, nationality) 


#mas victorias de pilotos españoles
mas_victorias_esp <- full_join(victorias_con_nombre, pilotos, c ("driverId" = "driverId")) %>% filter(nationality.x == "Spanish") %>%  select(driverId, driverRef.x, n_victorias, nationality.x) 


#-------------------------------------------------------------------------------------------

Escuderías campeonas

Por tamaño

#datos de escuderias pa quien quiera hacer algo
#escuderias <- rio::import(file = "./datos/constructors.csv")
#escuderias2 <- rio::import(file = "./datos/constructor_standings.csv")
#result_escuderias <- rio::import(file = "./datos/constructor_results.csv")

#pilotos <- rio::import(file = "./datos/drivers.csv")
#resultados <- rio::import(file = "./datos/results.csv")
#carreras <- rio::import(file = "./datos/races.csv")
#escuderiasesp <- escuderias %>% filter(nationality == "Spanish") #escuderias españolas

#campeones_esc <- full_join(pilotos, resultados, c("driverId" = "driverId")) %>% full_join(., carreras, c("raceId" = "raceId")) %>% select(driverId, driverRef, nationality, constructorId, points, year, round) %>% full_join(., escuderias, c("constructorId" = "constructorId")) %>% select(driverId, driverRef, nationality.x, constructorId, points, year, name, round) %>%  group_by(year, driverRef) %>%  mutate(puntos_totales = cumsum(points)) %>% ungroup() %>% group_by(year) %>% slice_max(puntos_totales, n=1) %>% select(name, driverRef) %>% group_by(name, driverRef) %>% mutate(total_camp = sum( NN = n())) %>% arrange(name) 


#campeones_esc <- campeones_esc[!(campeones_esc$driverRef == 'max_verstappen'),] 

#library(treemap)
#library(d3treeR)


# basic treemap
#gg_esc_campeones <- treemap(campeones_esc,
            #index=c("name","driverRef"),
            #vSize="total_camp",
            #type="index",
            #vColor = "name",
            #fontsize.labels=c(25,17),
            #bg.labels=c("transparent"),
            #palette = "Set2",
            #align.labels=list(
              #c("center", "center"), 
              #c("center", "bottom")),
            #title = "Escuderías con más campeones",
            #title.legend = "Escuderías")   
inter_camp <- d3tree2(gg_esc_campeones ,  rootname = "Escuderías y Campeones")
inter_camp

En valores absolutos

campeones_esc <- full_join(pilotos, resultados, c("driverId" = "driverId")) %>% full_join(., carreras, c("raceId" = "raceId")) %>% select(driverId, driverRef, nationality, constructorId, points, year, round) %>% full_join(., escuderias, c("constructorId" = "constructorId")) %>% select(driverId, driverRef, nationality.x, constructorId, points, year, name, round) %>%  group_by(year, driverRef) %>%  mutate(puntos_totales = cumsum(points)) %>% ungroup() %>% group_by(year) %>% slice_max(puntos_totales, n=1) %>% ungroup() %>% count(name) %>% arrange(desc(n))


ggplot(campeones_esc, aes(x = reorder(name,n),y = n)) + geom_bar(stat = "identity") + coord_flip()

Alonso (el nano)

#alonso vs compañeros de equipo

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
escuderias <- rio::import(file = "./datos/constructors.csv")
carreras <- rio::import(file = "./datos/races.csv")

alovsall <- full_join(pilotos, resultados, c ("driverId" = "driverId")) %>%  select(driverRef, resultId, raceId, constructorId, position, points) %>% full_join(., escuderias, c ("constructorId" = "constructorId")) %>% select(driverRef, resultId, raceId, constructorId, position, position, points, name) %>% full_join(., carreras, c ("raceId" = "raceId")) %>% select(driverRef, resultId, raceId, constructorId, position, position, points, name.x, year, round)

#alo_vs_marques <- alovsall %>% filter(year == 2001, driverRef %in% c("alonso", "marques"), round <= 14)

alo_vs_trulli <- alovsall %>% filter(year %in% c(2003, 2004), driverRef %in% c("alonso", "trulli")) %>% slice(1:15, 17:67) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_fisichella <- alovsall %>% filter(year %in% c(2005, 2006), driverRef %in% c("alonso", "fisichella"))  %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_hamilton <- alovsall %>% filter(year %in% c(2007) ,driverRef %in% c("alonso", "hamilton"))  %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_piquet <- alovsall %>% filter(year %in% c(2008, 2009), driverRef %in% c("alonso", "piquet_jr")) %>% slice(1:28, 36:63) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

#alo_vs_grosjean <- alovsall %>% filter(year == 2009, driverRef %in% c("alonso", "grosjean"), round >= 11)

alo_vs_massa <- alovsall %>% filter(year %in% c(2010, 2011, 2013), driverRef %in% c("alonso", "massa")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_raikkonen <- alovsall %>% filter(year == 2014, driverRef %in% c("alonso", "raikkonen")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_button <- alovsall %>% filter(year %in% c(2015, 2016), driverRef %in% c("alonso", "button")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_vandoorne <- alovsall %>% filter(year %in% c(2017, 2018), driverRef %in% c("alonso", "vandoorne")) %>% slice(1:45, 47:81) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points))%>% ungroup() 

alo_vs_ocon <- alovsall %>% filter(year == 2021, driverRef %in% c("alonso", "ocon")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

ALO_VS_ALL <- full_join(alo_vs_trulli, alo_vs_fisichella, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round" , "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_hamilton, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_piquet, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_massa, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados"))  %>% 
  full_join(., alo_vs_raikkonen, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_button, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_vandoorne, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_ocon, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados"))

objetos_no_borrar <- c("ALO_VS_ALL", "n_carreras_nom", "victorias_con_nombre")
rm(list = ls()[!ls() %in% objetos_no_borrar])


gc() #instruccion para que cargue el grafico, al ser tan complejo da error de no sé qué pero con esto funciona
#>           used (Mb) gc trigger  (Mb) max used  (Mb)
#> Ncells 1690466 90.3    3401015 181.7  3401015 181.7
#> Vcells 2998272 22.9    8388608  64.0  7889152  60.2
ggalo_vs_all <- ggplot(data = ALO_VS_ALL, aes(round, puntos_acumulados, color = driverRef)) +
  geom_line() +
  geom_point() + 
  labs(title = "Alonso contra el mundo",
       subtitle = "le das un carton con ruedas y aún te saca puntos",
       y = "Puntos", x = "") + facet_wrap( ~ year) + transition_reveal(round)

#ggalo_vs_all

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Campeones del mundo

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
escuderias <- rio::import(file = "./datos/constructors.csv")
carreras <- rio::import(file = "./datos/races.csv")



campeones <- full_join(pilotos, resultados, c("driverId" = "driverId")) %>% full_join(., carreras, c("raceId" = "raceId")) %>% select(driverId, driverRef, nationality, constructorId, points, year, round) %>% full_join(., escuderias, c("constructorId" = "constructorId")) %>% select(driverId, driverRef, nationality.x, constructorId, points, year, name, round) %>%  group_by(year, driverRef) %>%  mutate(puntos_totales = cumsum(points)) %>% ungroup() %>% group_by(year) %>% slice_max(puntos_totales, n=1) %>% ungroup() %>% group_by(driverRef)%>% mutate(total_campeonatos = sum(NN = n())) %>% distinct(driverRef, nationality.x, total_campeonatos) %>% arrange(nationality.x, total_campeonatos)
  
campeones <- campeones[!(campeones$driverRef == 'max verstappen'),]
id <- rownames(campeones)
campeones <- cbind(id=id, campeones)
campeones[, c(1)] <- sapply(campeones[, c(1)], as.numeric)




label_campeones <- campeones
number_of_bar <- nrow(label_campeones)

angle <- 90 - 360 * (label_campeones$id-0.5) /number_of_bar     # I substract 0.5 because the letter must have the angle of the center of the bars. Not extreme right(1) or extreme left (0)
label_campeones$hjust <- ifelse( angle < -90, 1, 0)
label_campeones$angle <- ifelse(angle < -90, angle+180, angle)



base_campeones <- campeones %>% 
  group_by(nationality.x) %>% 
  summarise(start=min(id), end=max(id)) %>% 
  rowwise() %>% 
  mutate(title=mean(c(start, end)))

grid_campeones <- base_campeones
grid_campeones$end <- grid_campeones$end[ c( nrow(grid_campeones), 1:nrow(grid_campeones)-1)] + 1
grid_campeones$start <- grid_campeones$start - 1
grid_campeones <- grid_campeones[-1,]

p <- ggplot(campeones, aes(x=as.factor(year), y=total_campeonatos, fill=nationality.x, color = nationality.x)) + geom_bar(aes(x=as.factor(id), y=total_campeonatos, fill=nationality.x), stat="identity", alpha=0.5) +
  
  geom_segment(data=grid_campeones, aes(x = 0, y = 8, xend = 31, yend = 8), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 6, xend = 31, yend = 6), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 4, xend = 31, yend = 4), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 2, xend = 31, yend = 2), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  
  annotate("text", x = rep(max(campeones$id),4), y = c(2, 4, 6, 8), label = c("2", "4", "6", "8") , color="white", size=3 , angle=0, fontface="bold", hjust=1) +
  
   geom_bar(aes(x=as.factor(id), y=total_campeonatos, fill=nationality.x), stat="identity", alpha=0.5) +
  ylim(-10,21) +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text = element_blank(),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    plot.margin = unit(rep(-1,4), "cm") ) +
  coord_polar() + 
  geom_text(data=label_campeones, aes(x=id, y=10, label=driverRef, hjust=hjust), color="white", fontface="bold",alpha=0.6, size=3.5, angle= label_campeones$angle, inherit.aes = FALSE ) +

  geom_segment(data=grid_campeones, aes(x = 0.70, y = -1, xend = 2.45, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE )  +
  geom_segment(data=grid_campeones, aes(x = 2.6, y = -1, xend = 3.55, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 3.65, y = -1, xend = 5.45, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 5.55, y = -1, xend = 7.35, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 7.5, y = -1, xend = 10.50, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 10.7, y = -1, xend = 19.20, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 19.4, y = -1, xend = 20.3, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 20.45, y = -1, xend = 23.4, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 23.65, y = -1, xend = 24.35, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 24.60, y = -1, xend = 27, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 27.2, y = -1, xend = 29.5, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 29.7, y = -1, xend = 30.5, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 30.7, y = -1, xend = 31.5, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 31.7, y = -1, xend = 32.5, yend = -1), colour = "white", alpha=1, size=0.3 , inherit.aes = FALSE ) + theme(legend.text = element_text(colour = "white"),
    legend.title = element_text( colour = "white"), 
     legend.background = element_rect(fill = "gray13", colour = "gray13"),
    legend.position = "right",
    panel.background = element_rect(fill = "gray13" , colour = "gray13"),
    plot.background = element_rect(fill = "gray13" , colour = "gray13"))
  


p

Templos y tiempos

carreras <- rio::import(file = "./datos/races.csv")
circuitos <- rio::import(file = "./datos/circuits.csv")

carreras_21 <- full_join(carreras,circuitos, c("circuitId" = "circuitId")) %>%
  filter(year=="2020") %>%
  select(round, name.x, name.y, date, location,country, lat, lng, alt) %>%
  arrange(round) %>% 
  mutate(round2 = round) 

carreras_21_v2 <- carreras_21[, c(1, 4, 10, 2, 3, 5, 6, 7, 8, 9)]
carreras_21_v2 <- carreras_21_v2%>%  unite(. ,variables, c(1, 5, 7), sep = "; ")

#pruebas para mapas, no ejecutar de momento
#library(widgetframe)
#library(leaflet)
#l <- leaflet() %>% setView(lat = 45.61560, lng = 9.281110, zoom=1)
#frameWidget(l) 

#mapaCiudadyPueblomayorinciAcu <- leaflet() %>%
 # setView(lng = -0.243591, lat = 38.821, zoom = 7) %>% 
  #addMarkers(lng = -0.243591, lat = 38.821 , popup = "Vall de Gallinera")%>%
  #setView(lng = -0.418598, lat = 40.2011, zoom = 7) %>% 
  #addMarkers(lng = -0.418598, lat = 40.2011 , popup = "Villahermosa del rio") %>% addTiles()
#mapaCiudadyPueblomayorinciAcu

#MAPA DEL MUNDO DE LA OSTIA NO TOCAR, pongo como comentario para que no tarde tanto al knitear


globo_circ <-create_globe() %>% globe_pov(45.61560, 9.281110) %>% globe_bars(coords(lat, lng, label  = variables, color = round2), data = carreras_21_v2)  %>% scale_bars_color()
#globo_circ

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])
circuitos <- rio::import(file = "./datos/circuits.csv")
tiempos <- rio::import(file = "./datos/lap_times.csv")
carreras <- rio::import(file = "./datos/races.csv")
pilotos <- rio::import(file = "./datos/drivers.csv")

circuitos_gp <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) #asocio las carreras a su circuito
tiemposvuelta_x_carrera <- full_join(circuitos_gp, tiempos, c ("raceId" = "raceId")) #tiempo de las vueltas por cada carrera

tiemposvuelta_x_carrera <- full_join(tiemposvuelta_x_carrera, pilotos, c ("driverId" = "driverId")) %>%  select(circuitId, name.y, driverId, driverRef, time.y, lap,position, year, country) #fusiono con el df de pilotos para asociar cada vuelta al nombre del piloto que la hizo


#calculo el record de cada circuito, filtrando el minimo de los tiempos en cada circuito
record_de_circuito <- tiemposvuelta_x_carrera %>% group_by(name.y) %>% slice_min(time.y, n=1)

#numero de records de circuito que tiene cada piloto
record_x_piloto <- record_de_circuito %>% group_by(driverId) %>% mutate(numero_records = sum(n())) %>% select(driverId, driverRef, numero_records) %>% distinct(driverRef, numero_records) %>% arrange(desc(numero_records))


# meter mapa del mundo con la ubicacion de los circuitos en la temporada 2021
#------------------------------------------------------------

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Capítulo oscuro del deporte

#creo df de muertes de formula 1
muertesf1 <- data.frame(
  "orden" = 1:42,
  "driverRef" = c("Chet Miller", "Carl Scaraborough", "Onofre Marimon", "Manny Ayulo", "Bill Vukovich", "Alberto Ascari","Eugenio Castellotti", "Keith Andrews", "Pat O'Connor", "Luigi Musso", "Peter Collins", "Stuart Lewis-Evans", "Jerry Unser", "Bob Cortner", "Ivor Bueb", "Chris Bristow", "Alan Stacey", "Giulio Cabianca", "Wolfgang von Trips", "Carel Godin de Beaufort", "John Taylor", "Lorenzo Bandini", "Bob Anderson", "Jo Schlesser", "Gerhard Mitter", "Piers Courage", "Jochen Rindt", "Jo Siffert", "Roger Williamson", "François Cevert", "Peter Revson", "Helmuth Koinigg", "Mark Donohue", "Tom Pryce", "Ronnie Peterson", "Patrick Depailler", "Gilles Villeneuve", "Riccardo Paletti", "Elio de Angelis", "Roland Ratzenberger", "Ayrton Senna", "Jules Bianchi"),
  "nationality" = c("American", "American", "Argentine", "American", "American", "Italian","Italian", "American", "American", "Italian", "British", "British", "American", "American", "British", "British", "British", "Italian", "German", "Dutch", "British", "Italian" , "British", "French", "German", "British", "Austrian", "Swiss", "British", "French", "American", "Austrian", "American", "British", "Swedish", "French", "Canadian", "Italian", "Italian", "Austrian", "Brazilian", "French"),
  "fecha_muerte" = c(1953, 1953, 1954, 1955, 1955, 1955, 1957, 1957, 1958,1958,1958,1958,1959,1959,1959,1960, 1960,1961,1961,1964,1966, 1967, 1967,1968,1969, 1970,1970,1971,1973,1973,1974,1974,1975,1977,1978,1980,1982,1982,1986,1994,1994,2014))
#no pongo las comillas en los años para que se creen directamente como observaciones numericas

#creo un df con todos los años para luego fusionarlo, ya que no hay muertes todos los años 
anyos <- data.frame(
  "orden" = 1:71,
  "año" = c(1950:2020))

#sumatorio de las muertes por año
muertes_anyo <- muertesf1 %>% group_by(fecha_muerte) %>% mutate(muertesxanyo = sum(n())) %>% distinct(fecha_muerte, muertesxanyo) 

#fusiono los 2 dfs para que tenga en cuenta los años donde no hay muertes
muertesf1_final <- full_join(muertes_anyo, anyos, c("fecha_muerte" = "año")) %>% select(fecha_muerte,muertesxanyo) %>% arrange(fecha_muerte)

#convierto los N/A en 0, es decir, cuando no hay observaciones, ha habido 0 muertes
muertesf1_final[is.na(muertesf1_final)] <- 0

#grafico de las muertes por cada año + la tendencia negativa ea lo largo de la historia
gg_muertes <- ggplot(muertesf1_final, aes(x = fecha_muerte, y = muertesxanyo )) +  geom_bar(stat = "identity", fill = "white", colour = "white") + geom_smooth(colour = "cyan", se = FALSE) + labs(x = "Año" , y = "Número de muertes")  + theme(axis.line = element_line(colour = "white"),
    axis.ticks = element_line(colour = "white"),
    panel.grid.major = element_line(colour = "gray13"),
    panel.grid.minor = element_line(colour = "gray13"),
    axis.title = element_text(colour = "white"),
    axis.text = element_text(colour = "white"),
    plot.title = element_text(colour = "white"),
    panel.background = element_rect(fill = "gray12",
        colour = "white"), plot.background = element_rect(fill = "gray13")) +labs(colour = "white") + theme(panel.grid.major = element_line(colour = "gray38",
    linetype = "dotted"), panel.grid.minor = element_line(colour = NA),
    plot.title = element_text(size = 25,
        hjust = 0.5)) +labs(title = "Accidentes mortales por año") + geom_text(data = data.frame(x = 2004.10522642875, y = 0.237450516942241, 
    label = "Tendencia negativa"), mapping = aes(x = x, y = y, 
    label = label), colour = "cyan", inherit.aes = FALSE, size = 3)

ggplotly(gg_muertes)

1. Introducción

Tenemos pensado elaborar el trabajo en equipo sobre Formula 1, una competición de la que somos muy aficionados, entre otras cosas por la importancia que tienen los datos a la hora de formalizar las estrategias en la competición.

Remando a contracorriente

#mas posiciones remontadas en una carrera gran premio

tiempos <- rio::import(file = "./datos/lap_times.csv")
carreras <- rio::import(file = "./datos/races.csv")
resultados <- rio::import(file = "./datos/results.csv")
circuitos <- rio::import(file = "./datos/circuits.csv")
pilotos <- rio::import(file = "./datos/drivers.csv")

resultados[, c(6,9)] <- sapply(resultados[, c(6,9)], as.numeric) #transformo variables grid y positionOrder en numerico
str(resultados) # para comprobarlo
#> 'data.frame':    25140 obs. of  18 variables:
#>  $ resultId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ raceId         : int  18 18 18 18 18 18 18 18 18 18 ...
#>  $ driverId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ constructorId  : int  1 2 3 4 1 3 5 6 2 7 ...
#>  $ number         : chr  "22" "3" "7" "5" ...
#>  $ grid           : num  1 5 7 11 3 13 17 15 2 18 ...
#>  $ position       : chr  "1" "2" "3" "4" ...
#>  $ positionText   : chr  "1" "2" "3" "4" ...
#>  $ positionOrder  : num  1 2 3 4 5 6 7 8 9 10 ...
#>  $ points         : num  10 8 6 5 4 3 2 1 0 0 ...
#>  $ laps           : int  58 58 58 58 58 57 55 53 47 43 ...
#>  $ time           : chr  "1:34:50.616" "+5.478" "+8.163" "+17.181" ...
#>  $ milliseconds   : chr  "5690616" "5696094" "5698779" "5707797" ...
#>  $ fastestLap     : chr  "39" "41" "41" "58" ...
#>  $ rank           : chr  "2" "3" "5" "7" ...
#>  $ fastestLapTime : chr  "1:27.452" "1:27.739" "1:28.090" "1:28.603" ...
#>  $ fastestLapSpeed: chr  "218.300" "217.586" "216.719" "215.464" ...
#>  $ statusId       : int  1 1 1 1 1 11 5 5 4 3 ...
#mayores remontadas de la historia, se resta posicion de salida - posicion final
puestos_remontados <- resultados %>% mutate(remontados = grid - positionOrder) %>% select(raceId, driverId, grid, positionOrder, remontados) 


#de toda la historia
circuitos_gp <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) %>% select(circuitId, name.y, raceId, year)

ptos_remont_carrera <- inner_join(puestos_remontados, circuitos_gp)

puestos_remont_piloto <- full_join(pilotos, ptos_remont_carrera, c("driverId" = "driverId")) %>% slice_max(remontados, n=10) %>% select(driverId, driverRef,name.y,year, raceId, grid, positionOrder, remontados)

#------------------------------


# de la hisotoria reciente
circuitos_gp_recient <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) %>% select(circuitId, name.y, raceId, year) %>% filter(year >= 1995)

ptos_remont_carrera_recient <- inner_join(puestos_remontados, circuitos_gp_recient)

puestos_remont_piloto_recient <- full_join(pilotos, ptos_remont_carrera_recient, c("driverId" = "driverId")) %>% slice_max(remontados, n=10) %>% select(driverId, driverRef, name.y, year,raceId, grid, positionOrder, remontados) %>% slice(1:4,6:8,10) %>% arrange(desc(remontados))

ggremontados <- ggplot(puestos_remont_piloto_recient, aes(x = reorder(driverRef, remontados), remontados)) + geom_bar(stat = "identity") + coord_flip() + labs(x = "Pilotos", y = "Nº de puestos remontados" )
ggremontados

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

ALONSO vs HAMILTON

#se necesita tener cargado "n_carreras_nom", "victorias_con_nombre"
#1234
fotos_ALO_vs_HAM <- c("./imagenes/pilotos/alonso.png", "./imagenes/pilotos/hamilton.png")
fotos_esp_ing <- c("./imagenes/paises/espanya.png", "./imagenes/paises/uk.png")
n_carreras_alo_ham <- n_carreras_nom %>% filter(driverRef %in% c("alonso", "hamilton"))

n_victorias_alo_ham <- victorias_con_nombre %>% filter(driverRef %in% c("alonso", "hamilton"))

alo_vs_ham <- full_join(n_carreras_alo_ham, n_victorias_alo_ham, c("driverRef"= "driverRef")) %>% select( driverRef, numero_carreras, n_victorias) %>% add_column(fotos_esp_ing, fotos_ALO_vs_HAM) 

library(gt)
alo_vs_ham_tabla <- alo_vs_ham %>% gt() %>% text_transform( locations = cells_body(columns = c(fotos_esp_ing)), fn = function(x) {gt::local_image(x, height = 50)}) %>% text_transform( locations = cells_body(columns = c(fotos_ALO_vs_HAM)), fn = function(x) {gt::local_image(x, height = 100)}) %>% tab_header(title = md("**Alonso vs Hamilton**"), subtitle = md("Comparación")) %>%   cols_label(
    driverRef = html(""),
    numero_carreras = html("Nº carreras"),
    n_victorias = html("Nº victorias"),
    fotos_esp_ing = html("País"),
    fotos_ALO_vs_HAM = html("")) %>%  
  tab_options(table.background.color = "gray13",   table.font.color.light = "cyan") %>% 
  cols_align(align = "center",
  columns = everything())

alo_vs_ham_tabla
Alonso vs Hamilton
Comparación
Nº carreras Nº victorias País
alonso 323 32
hamilton 275 98
#audiencias


audiencias <- rio::import(file = "./datos/audienciasF1.csv")


gg_audiencias <- ggplot(audiencias, aes(x=year, y= numero_espectadores)) +
  geom_segment( aes(x=year, xend = year, y=0, yend= numero_espectadores , size = "1")) +
  geom_point( size=5, color="blue", fill=alpha("cyan", 8), alpha=0.7, shape=21, stroke=2) +  
  scale_x_continuous(
    breaks = seq(2004, 2020, 1),
    limits = c(2003, 2021)) + labs(x = "Año", y = "Numero de espectadores" )  + theme(panel.background = element_rect(fill = "gray13"),
    plot.background = element_rect(fill = "gray13")) + theme(axis.line = element_line(colour = "white"),
    panel.grid.major = element_line(colour = "gray20"),
    panel.grid.minor = element_line(colour = "gray20"),
    axis.text = element_text(colour = "white"),
    legend.position = "none") + theme(axis.title = element_text(colour = "white"),
    plot.title = element_text(colour = "white",
        hjust = 0.5)) +labs(title = "Evolución de la audiencia",
    colour = "white") + theme(axis.text.x = element_text(size = 4)) #+ transition_reveal(numero_espectadores)

ggplotly(gg_audiencias)
#presupuestos

presupuestos <- read_excel("datos/presupuestos.xlsx")

gg_presup <- ggplot(presupuestos, aes(year, Presupuesto, color = Escuderia)) + 
  geom_point() + geom_line() + 
  labs(x = "Año", y = "Presupuesto en €" ) +
    scale_x_continuous(
    breaks = seq(2015, 2023, 1),
    limits = c(2014, 2024)) + 
  scale_y_continuous( breaks = seq(0, 700000000, 100000000),
    limits = c(0, 600000000))  + theme(axis.ticks = element_line(colour = "white"),
    panel.grid.major = element_line(colour = "white",
        linetype = "blank"), panel.grid.minor = element_line(colour = "white",
        linetype = "blank"), axis.title = element_text(size = 14,
        face = "bold", colour = "cyan", vjust = 0.75),
    axis.text = element_text(colour = "white"),
    plot.title = element_text(size = 16,
        face = "bold", colour = "cyan", hjust = 0.5,
        vjust = 0.75), legend.text = element_text(face = "bold",
        colour = "cyan"), legend.title = element_text(size = 13,
        face = "bold", colour = "cyan"),
    panel.background = element_rect(fill = "gray12",
        colour = "white"), plot.background = element_rect(fill = "gray12"),
    legend.key = element_rect(fill = "gray12"),
    legend.background = element_rect(fill = "gray12")) +labs(title = "PRESUPUESTO DE CADA EQUIPO POR TEMPORADA") + theme(panel.grid.major = element_line(colour = NA),
    panel.grid.minor = element_line(colour = NA),
    axis.title = element_text(size = 11),
    plot.title = element_text(size = 14),
    legend.text = element_text(size = 9),
    legend.title = element_text(size = 11),
    panel.background = element_rect(fill = "gray12",
        colour = NA), plot.background = element_rect(fill = "gray12",
        colour = NA)) + theme(legend.key = element_rect(fill = "gray12"),
    legend.background = element_rect(fill = "gray12"))
 
ggplotly(gg_presup) #para que sea interactivo

THE PLAN

pilotos <- rio::import(file = "./datos/drivers.csv")

nacionalidad <- pilotos %>% group_by(nationality) %>% 
  mutate(numero_compatriotas = sum(n())) %>% 
  distinct(numero_compatriotas) %>% arrange(desc(numero_compatriotas)) %>% 
  mutate(country = case_when(nationality == "British" ~ "United Kingdom",
          nationality == "American" ~ "United States",
          nationality == "Italian" ~ 'Italy',
          nationality == "French" ~ 'France',
          nationality == "German" ~ 'Germany',
          nationality == "Brazilian" ~ 'Brazil',
          nationality == "Argentine" ~ 'Argentina',
          nationality == "Swiss" ~ 'Switzerland',
          nationality == "Belgian" ~ 'Belgium',
          nationality == "South African" ~ 'South Africa',
          nationality == "Japanese" ~ 'Japan',
          nationality == "Australian" ~ 'Australia',
          nationality == "Dutch" ~ 'Netherlands',
          nationality == "Spanish" ~ 'Spain',
          nationality == "Austrian" ~ 'Austria',
          nationality == "Canadian" ~ 'Canada',
          nationality == "Swedish" ~ 'Sweden',
          nationality == "Finnish" ~ 'Finland',
          nationality == "New Zealander" ~ 'New Zealand',
          nationality == "Mexican" ~ 'Mexico',
          nationality == "Irish" ~ 'Ireland',
          nationality == "Danish" ~ 'Denmark',
          nationality == "Portuguese" ~ 'Portugal',
          nationality == "Monegasque" ~ 'France',
          nationality == "Rhodesian" ~ 'Zimbabwe',
          nationality == "Uruguayan" ~ 'Uruguay',
          nationality == "Russian" ~ 'Russia',
          nationality == "Colombian" ~ 'Colombia',
          nationality == "Venezuelan" ~ 'Venezuela',
          nationality == "East German" ~ 'German',
          nationality == "Indian" ~ 'India',
          nationality == "Thai" ~ 'Thailand',
          nationality == "Polish" ~ 'Poland',
          nationality == "Hungarian" ~ 'Hungary',
          nationality == "Czech" ~ 'Czech Rep.',
          nationality == "Malaysian" ~ 'Malaysia',
          nationality == "Chilean" ~ 'Chile',
          nationality == "Liechtensteiner" ~ 'Switzerland',
          nationality == "American-Italian" ~ 'United States',
          nationality == "Argentine-Italian" ~ 'Argentina',
          nationality == "Indonesian" ~ 'Indonesia'))


library(tmap)
data(World)
world <- World; rm(World)

2. Datos

Hemos encontrado en kaggle bastantes conjuntos de datos con los que poder trabajar, pero especialmente este, que posee gran variedad de datos en lo referente a pilotos, resultados, circuitos, tiempos, etc… Consideramos que para empezar a trabajar será suficiente, y en función de como vayamos dirigiendo el trabajo, buscaremos diferentes conjunto de datos con los que apoyarnos.

3. Trabajos en los que nos vamos a basar

Con los datos que hemos encontrado, existen una serie de códigos que ya trabajan con estos datos, especialmente este, que ha conseguido realizar análisis con este conjunto de datos y varias ilustraciones muy llamativas, por lo que podremos tomarlo como referencia durante el inicio del trabajo

“Nadie es más rápido que el nano”

LS0tDQp0aXRsZTogIkFsdCArIEZvcm11bGEgMSINCmF1dGhvcjogIkNheWV0YW5vIFJvbWVybyBNb250ZWFndWRvIChjYXJvbW9uM0BhbHVtbmkudXYuZXMpICBcblxuIEFsZWphbmRybyBHYXJjw61hIFNlZ2FycmEgKGFnYXJzZTRAYWx1bW5pLnV2LmVzKSAgXG4gXG4gQ2FybG9zIEdhcmPDrWEgQ2FzdGlsbGEgKGdhcmNhczhAYWx1bW5pLnV2LmVzKS4gXG5cbiBVbml2ZXJzaXRhdCBkZSBWYWzDqG5jaWEiDQpkYXRlOiAiRGljaWVtYnJlIGRlIDIwMjEgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgICNjc3M6ICIuL2Fzc2V0cy9teV9jc3NfZmlsZS5jc3MiDQogICAgdGhlbWU6IGRhcmtseQ0KICAgIGhpZ2hsaWdodDogdGFuZ28gDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzIA0KICAgIHRvY19mbG9hdDogDQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCjwhLS0tIGNodW5rIHBhcmEgcXVlIGxhIGZ1ZW50ZSBzZWEgbGEgb2ZpY2lhbCBkZSBsYSBGb3JtdWxhIDEgLS0+DQpgYGB7Y3NzLCBlY2hvPUZBTFNFfSANCkBmb250LWZhY2Ugew0KICBmb250LWZhbWlseTogRjE7DQogIHNyYzogdXJsKGh0dHBzOi8vd3d3LmZvcm11bGExLmNvbS9ldGMvZGVzaWducy9mb20td2Vic2l0ZS9mb250cy9GMVJlZ3VsYXIvRm9ybXVsYTEtUmVndWxhci50dGYpOw0KfQ0KDQpzcGFuew0KICBmb250LWZhbWlseTogRjE7DQp9DQoNCmF7DQogIGZvbnQtZmFtaWx5OiBGMTsNCn0NCg0KLm5hdi1waWxscz5saS5hY3RpdmU+YTpmb2N1cyB7DQogICAgY29sb3I6ICNmZmZmZmY7DQogICAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmF5Ow0KfQ0KDQouY29udGFpbmVyLWZsdWlkLCAuY29udGFpbmVyLWZsdWlkIGgxIHsNCiAgICBmb250LWZhbWlseTogRjE7DQogICAgbGluZS1oZWlnaHQ6IDEuNzsNCn0NCg0KLmNvbnRhaW5lci1mbHVpZCBwIHsNCiAgICBmb250LWZhbWlseTogRjE7DQogICAgY29sb3I6Y3lhbjsNCn0NCg0KaDEsaDIsaDMsaDQsaDUsaDYscCwgdGFibGUgew0KICBmb250LWZhbWlseTogRjE7DQogIGNvbG9yOiBjeWFuDQp9DQpgYGANCg0KDQoNCmBgYHtyIGNodW5rLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgI3Jlc3VsdHMgPSAiaG9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLA0KICAgICAgICAgICAgICAgICAgICAgICNmaWcud2lkdGggPSA3LCAjZmlnLmhlaWdodD0gNywgICANCiAgICAgICAgICAgICAgICAgICAgICAjb3V0LndpZHRoID0gNywgb3V0LmhlaWdodCA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFzcCA9IDAuNjI4LCBvdXQud2lkdGggPSAiNzUlIiwgZmlnLmFsaWduID0gImNlbnRlciIpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZGV2ID0gInBuZyIsIGRldi5hcmdzID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKQ0KYGBgDQoNCmBgYHtyIG9wdGlvbnMtc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjLSBwYXJhIHF1aXRhciBsYSBub3RhY2nDs24gY2llbnTDrWZpY2ENCm9wdGlvbnMoInlhbWwuZXZhbC5leHByIiA9IFRSVUUpIA0KYGBgDQoNCg0KYGBge3Iga2xpcHB5LCBlY2hvID0gRkFMU0V9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpDQpgYGANCg0KDQoNCjxociBjbGFzcz0ibGluZWEtYmxhY2siPg0KDQo8IS0tIEVsIHDDoXJyYWZvIGRlIGFiYWpvIGhhcyBkZSBkZWphcmxvIGNhc2kgaWd1YWwsIHNvbG8gSEFTIGRlIFNVU1RJVFVJUiAicGVyZXpwNDQiIHBvciB0dSB1c3VhcmlvIGRlIEdpdGh1Yi0tPg0KVHJhYmFqbyBlbGFib3JhZG8gcGFyYSBsYSBhc2lnbmF0dXJhICJQcm9ncmFtYWNpw7NuIHkgbWFuZWpvIGRlIGRhdG9zIGVuIGxhIGVyYSBkZWwgQmlnIERhdGEiIGRlIGxhIFVuaXZlcnNpdGF0IGRlIFZhbMOobmNpYSBkdXJhbnRlIGVsIGN1cnNvIDIwMjEtMjAyMi4gRWwgcmVwbyBkZWwgdHJhYmFqbyBlc3TDoSBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9jYXlldGFubzEwOC90cmFiYWpvX0JpZ0RhdGFfZXF1aXBvKXt0YXJnZXQ9Il9ibGFuayJ9LiANCg0KPCEtLSBFbCBww6FycmFmbyBkZSBhYmFqbyBoYXMgZGUgZGVqYXJsbyBleGFjdGFtZW50ZSBpZ3VhbCwgTk8gaGFzIGRlIGNhbWJpYXIgbmFkYS0tPg0KDQpMYSBww6FnaW5hIHdlYiBkZSBsYSBhc2lnbmF0dXJhIHkgbG9zIHRyYWJham9zIGRlIG1pcyBjb21wYcOxZXJvcyBwdWVkZW4gdmVyc2UgW2FxdcOtXShodHRwczovL3BlcmV6cDQ0LmdpdGh1Yi5pby9pbnRyby1kcy0yMS0yMi13ZWIvMDctdHJhYmFqb3MuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4NCg0KPGhyIGNsYXNzPSJsaW5lYS1yZWQiPg0KDQojIExpYnJlcsOtYSBkZSBwYXF1ZXRlcw0KDQpgYGB7ciBwYWNrYWdlcy1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtsaXBweSkgICMtIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJybGVzdXIva2xpcHB5IikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHJpbykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShnZ1RoZW1lQXNzaXN0KQ0KbGlicmFyeShnbG9iZTRyKSAgI3JlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJKb2huQ29lbmUvZ2xvYmU0ciIpDQpsaWJyYXJ5KHJlbW90ZXMpIA0KbGlicmFyeShndCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHRyZWVtYXApICNpbnN0YWxsLnBhY2thZ2VzKHRyZWVtYXApDQpsaWJyYXJ5KGQzdHJlZVIpICNyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZDN0cmVlUi9kM3RyZWVSIikNCmBgYA0KDQojIENhbXBlw7NuIGRlbCBtdW5kbw0KDQoNCg0KIyBIaXN0b3JpYSBkZSBsYSBGMQ0KDQoNCiMgTE9HT1MgREUgTEEgRk9STVVMQSAxICB7LnRhYnNldH0NCg0KIyMgTG9nbyBkZSAxOTg1IGEgMTk4Ng0KDQoNCjxjZW50ZXI+DQohW0xvZ28gZGUgMTk4NSBhIDE5ODZdKC4vaW1hZ2VuZXMvbG9nb3MvMTk4NS0xOTg2LnBuZyl7d2lkdGg9NDAwIGhlaWdodD03MH0NCjwvY2VudGVyPg0KDQoNCg0KDQoNCg0KIyMgTG9nbyBkZSAxOTg3IGEgMTk5Mw0KPGNlbnRlcj4NCiFbTG9nbyBkZSAxOTg3IGEgMTk5M10oLi9pbWFnZW5lcy9sb2dvcy8xOTg3LTE5OTMuanBnKXt3aWR0aD0yNTAgaGVpZ2h0PTMwMH0NCjwvY2VudGVyPg0KDQojIyBMb2dvIGRlIDE5OTQgYSAyMDE3DQo8Y2VudGVyPg0KIVtMb2dvIGRlIDE5OTQgYSAyMDE3XSguL2ltYWdlbmVzL2xvZ29zLzE5OTQtMjAxNy5wbmcpe3dpZHRoPTQ1MCBoZWlnaHQ9MzAwfQ0KPC9jZW50ZXI+DQojIyBMb2dvIEFjdHVhbA0KPGNlbnRlcj4NCiFbTE9HTyBkZXNkZSAyMDE4IGhhc3RhIGxhIGFjdHVhbGlkYWRdKC4vaW1hZ2VuZXMvbG9nb3MvMjAxOC0ucG5nKXt3aWR0aD00MDAgaGVpZ2h0PTMwMH0NCjwvY2VudGVyPg0KDQojIFByb3RhZ29uaXN0YXMNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQoNCiMtLS1QUkVQQVJBQ0lPTiBERSBMT1MgREFUT1MNCg0KcGlsb3RvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvZHJpdmVycy5jc3YiKQ0KcmVzdWx0YWRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmVzdWx0cy5jc3YiKQ0KI3N0cihyZXN1bHRhZG9zKQ0KI3N0cihwaWxvdG9zKQ0KI3BpbG90b3NbLCBjKDEpXSA8LSBzYXBwbHkocGlsb3Rvc1ssIGMoMSldLCBhcy5udW1lcmljKQ0KI3Jlc3VsdGFkb3NbLCBjKDMsNiw5KV0gPC0gc2FwcGx5KHJlc3VsdGFkb3NbLCBjKDMsNiw5KV0sIGFzLm51bWVyaWMpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojbnVtZXJvIGRlIGNhcnJlcmFzIHF1ZSBoYSBjb3JyaWRvIGNhZGEgcGlsb3RvDQoNCiNleHBsaWNhY2lvbiBkZSBsbyBxdWUgaGFnbywgbm8gc2UgcG9ycXVlIGxhIHZhcmlhYmxlIHN1bWF0b3JpbyBubyBsYSBkZXRlY3RhIGNvbW8gbnVtZXJpY2EgYXVucXVlIGxhIHBhc2UgYSBudW1lcmljYSwgcG9yIHRhbnRvIGFsIG9yZGVuYXIgY29uIHNsaWNlIG1heCBubyBmdW5jaW9uYSwgbG8gcXVlIGhlIGhlY2hvIGVzIHVzYXIgbGEgZnVuY2lvbiBhcnJhbmdlLCBxdWUgb3JkZW5hIGRlIG1lbm9yIGEgbWF5b3IsIHBlcm8gY29tbyBxdWVyZW1vcyBsb3MgcXVlIG1hcyBjYXJyZXJhcyBoYW4gY29ycmlkbywgbm8gbWUgc2lydmUgZGUgbWVub3IgYSBtYXlvciwgcG9yIHRhbnRvIGhlIG11bHRpcGxpY2FkbyBsYSB2YXJpYWJsZSBkZWwgc3VtYXRvcmlvIHBvciAtMSwgaGUgdXNhZG8gYXJyYW5nZSBwYXJhIHF1ZSBsb3MgcXVlIG3DoXMgY2FycmVyYXMgdGllbmVuIHNhbGdhbiBwcmltZXJvLCB5IGx1ZWdvIGhlIHZ1ZWx0byBhIG11bHRpcGxpY2FyIHBvciAtMS4gbHVlZ28gaGUgY29naWRvIG1heW9yZXMgZGUgMjAyIGNhcnJlcmFzLCBxdWUgc29uIGxvcyAyMCBxdWUgbcOhcyB0aWVuZW4sIHBvcnF1ZSBzaSBoYWdvIHNsaWNlIHNlIGRlc2N1YWRyYSB5IHRlIGRldnVlbHZlIGVsIGRmIGRlbCBwcmluY2lwaW8NCg0Kbl9jYXJyZXJhcyA8LSByZXN1bHRhZG9zICU+JSBncm91cF9ieShkcml2ZXJJZCkgJT4lIG11dGF0ZShudW1lcm9fY2FycmVyYXMgPSBzdW0obigpKSkgJT4lIGRpc3RpbmN0KG51bWVyb19jYXJyZXJhcykgJT4lIGFycmFuZ2UoZGVzYyhudW1lcm9fY2FycmVyYXMpKSAjbXV0YXRlKG51bWVyb19jYXJyZXJhc19maW5hbCA9IG51bWVyb19jYXJyZXJhcyotMSkNCg0Kbl9jYXJyZXJhc19ub20gPC0gZnVsbF9qb2luKG5fY2FycmVyYXMsIHBpbG90b3MsIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBudW1lcm9fY2FycmVyYXMpICAlPiUgIGZpbHRlcihudW1lcm9fY2FycmVyYXMgPj0gMjAyICkgI2xvcyAyMCBxcXVlIG1hcyBjYXJyZXJhcyB0aWVuZW4gKG5vIGZ1bmNpb25hIHVzYXIgc2xpY2VfbWF4KQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBudW1lcm8gZGUgdmljdG9yaWFzIHBvciBwaWxvdG8NCnZpY3RvcmlhcyA8LSByZXN1bHRhZG9zICU+JSBmaWx0ZXIocG9zaXRpb24gPT0gIjEiKSAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUobl92aWN0b3JpYXMgPSBzdW0obigpKSkgJT4lIGRpc3RpbmN0KG5fdmljdG9yaWFzKSAlPiUgYXJyYW5nZShkZXNjKG5fdmljdG9yaWFzKSkjbXV0YXRlKG5fdmljdG9yaWFzX2ZpbmFsID0gbl92aWN0b3JpYXMqLTEpDQoNCiNhcXVpIGZ1c2lvbm8gY29uIGVsIGRmIGRlIHBpbG90b3MgcGFyYSBxdWUgYXBhcmV6Y2EgZWwgbm9tYnJlIHkgbm8gc8OzbG8gZWwgSUQgZGVsIHBpbG90byBlbiBjdWVzdGlvbiwgeSBoYWdvIGxvIG1pc21vIHF1ZSBlbiBlbCBhcGFydGFkbyBkZSBhcnJpYmEgcGFyYSBvcmRlbmFyDQp2aWN0b3JpYXNfY29uX25vbWJyZSA8LSBmdWxsX2pvaW4odmljdG9yaWFzLCBwaWxvdG9zLCBjICgiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIG5hdGlvbmFsaXR5LCBkcml2ZXJSZWYsIG5fdmljdG9yaWFzKSAgI2xvcyAxMCBjb24gbWFzIHZpY3RvcmlhcywgdG1wIGZ1bmNpb25hIHNsaWNlX21heA0KbWFzX3ZpY3RvcmlhcyA8LSB2aWN0b3JpYXNfY29uX25vbWJyZSAlPiUgIGZpbHRlcihuX3ZpY3RvcmlhcyA+PSAyNSApIA0KDQoNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojcmVzdWx0YWRvIG1lZGlvDQpzdHIocmVzdWx0YWRvcykNCg0KcmVzdWx0YWRvc1ssIGMoNyldIDwtIHNhcHBseShyZXN1bHRhZG9zWywgYyg3KV0sIGFzLm51bWVyaWMpDQpyZXN1bHRhZG9zW2lzLm5hKHJlc3VsdGFkb3MpXSA8LSAyNSANCg0KcmVzdWx0YWRvX21lZGlvIDwtICBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBwb3NpdGlvbikgJT4lIGdyb3VwX2J5KGRyaXZlcklkKSAlPiUgbXV0YXRlKHJlc3VsdF9tZWRpbyA9IG1lYW4ocG9zaXRpb24pKSAlPiUgZGlzdGluY3QgKGRyaXZlcklkLCBkcml2ZXJSZWYsIHJlc3VsdF9tZWRpbykgJT4lIGFycmFuZ2UocmVzdWx0X21lZGlvKQ0KDQojcmVzdWx0YWRvIG1lZGlvIGVuIGNsYXNpZmljYWNpb24NCnJlc3VsdGFkb19tZWRpb19jbGFzIDwtICBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBncmlkKSAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUocmVzdWx0X21lZGlvX2NsYXMgPSBtZWFuKGdyaWQpKSAlPiUgZGlzdGluY3QgKGRyaXZlcklkLCBkcml2ZXJSZWYsIHJlc3VsdF9tZWRpb19jbGFzKSAgJT4lIGZpbHRlcihyZXN1bHRfbWVkaW9fY2xhcyA+IDApICU+JSBhcnJhbmdlKHJlc3VsdF9tZWRpb19jbGFzKSAgDQoNCg0KI251bWVybyBkZSB2dWVsdGFzIGxpZGVyYW5kbw0KDQoNCiNwdW50b3MgcG9yIGNhcnJlcmEgKHB1bnRvcy9jYXJyZXJhKQ0KDQpwdW50b3NfeF9jYXJyZXJhIDwtICBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBwb2ludHMpICU+JSBmdWxsX2pvaW4oLiwgbl9jYXJyZXJhcywgIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIGdyb3VwX2J5KGRyaXZlcklkKSAlPiUgbXV0YXRlKHRvdGFsX3B1bnRvcyA9IHN1bShwb2ludHMpKSAlPiUgZGlzdGluY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbnVtZXJvX2NhcnJlcmFzLCB0b3RhbF9wdW50b3MpICU+JSBtdXRhdGUobWVkaWFfcHVudG9zID0gdG90YWxfcHVudG9zL251bWVyb19jYXJyZXJhcykgJT4lIGFycmFuZ2UoZGVzYyhtZWRpYV9wdW50b3MpKQ0KDQpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJ2aWN0b3JpYXNfY29uX25vbWJyZSIsICJuX2NhcnJlcmFzX25vbSIsICJtYXNfdmljdG9yaWFzIikNCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQ0KYGBgDQoNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQoNCiNzZSBuZWNlc2l0YSAibWFzX3ZpY3RvcmlhcyINCg0KDQpnZ19tYXNfdmljdG9yaWFzIDwtIGdncGxvdChtYXNfdmljdG9yaWFzLCBhZXMoeCA9IHJlb3JkZXIoZHJpdmVyUmVmLCBuX3ZpY3RvcmlhcyksIHkgPSBuX3ZpY3RvcmlhcyApKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGxhYnMoeCA9ICJQaWxvdG8iICwgeSA9ICJOw7ptZXJvIGRlIHZpY3RvcmlhcyIpDQpnZ19tYXNfdmljdG9yaWFzDQojdHJhYmFqbyAtLT4gZGFybGUgZm9ybWF0byBjaHVsbywgeWEgdmVyZW1vcyBlc3RlIHB1ZW50ZSBzaSBsZSBwb2RlbW9zIG1ldGVyIGRpbsOhbWljbyBvIHF1ZQ0KDQogICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQoNCiMjIFBhcnJpbGxhIGRlIHBpbG90b3MgMjAyMQ0KDQpgYGB7cn0NCiNmb3Rvc19waWxfMjAyMSA8LSBjKCIuL2ltYWdlbmVzL3BpbG90b3MvaGFtaWx0b24ucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9ib3R0YXMucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy92ZXJzdGFwcGVuLmpuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvcGVyZXoucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9zYWluei5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL2xlY2xlcmMucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9ub3JyaXMucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9yaWNjaWFyZG8ucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy92ZXR0ZWwucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9zdHJvbGwucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9hbG9uc28ucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9vY29uLmpuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvZ2FzbHkucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy90c3Vub2RhLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvcnVzc2VsbC5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL2xhdGlmaS5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3JhaWtrb25lbi5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL2dpb3ZpbmF6emkuam5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9taWNrLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvbWF6ZXBpbi5wbmciKQ0KDQojZm90b3NfcGFpc18yMDIxIDwtIGMoIi4vaW1hZ2VuZXMvcGlsb3Rvcy9oYW1pbHRvbi5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL2JvdHRhcy5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3ZlcnN0YXBwZW4uam5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9wZXJlei5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3NhaW56LnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvbGVjbGVyYy5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL25vcnJpcy5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3JpY2NpYXJkby5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3ZldHRlbC5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3N0cm9sbC5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL2Fsb25zby5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL29jb24uam5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9nYXNseS5wbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL3RzdW5vZGEucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9ydXNzZWxsLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvbGF0aWZpLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvcmFpa2tvbmVuLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvZ2lvdmluYXp6aS5qbmciLCAiLi9pbWFnZW5lcy9waWxvdG9zL21pY2sucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9tYXplcGluLnBuZyIpDQoNCiNmb3Rvc19lc2NfMjAyMSA8LSBjKCIuL2ltYWdlbmVzL2VzY3VkZXJpYXMvbWVyY2VkZXMucG5nIiwiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL21lcmNlZGVzLnBuZyIsIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9yZWRidWxsLnBuZyIsICIuL2ltYWdlbmVzL2VzY3VkZXJpYXMvcmVkYnVsbC5wbmciLCIuL2ltYWdlbmVzL2VzY3VkZXJpYXMvZmVycmFyaS5wbmciLCAiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL2ZlcnJhcmkucG5nIiwiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL21jbGFyZW4ucG5nIiwgIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9tY2xhcmVuLnBuZyIsIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hc3Rvbi5wbmciLCAiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL2FzdG9uLnBuZyIsIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hbHBpbmUucG5nIiwgIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hbHBpbmUucG5nIiwiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL2FscGhhdGF1cmkucG5nIiwgIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hbHBoYXRhdXJpLnBuZyIsIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy93aWxsaWFtcy5wbmciLCAiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL3dpbGxpYW1zLnBuZyIsIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hbGZhcm9tZW8ucG5nIiwgIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9hbGZhcm9tZW8ucG5nIiwiLi9pbWFnZW5lcy9lc2N1ZGVyaWFzL2hhYXMucG5nIiwgIi4vaW1hZ2VuZXMvZXNjdWRlcmlhcy9oYWFzLnBuZyIsKQ0KDQoNCiNsaWJyYXJ5KGd0KQ0KI211bmRpYWxfMjAyMSA8LSBkYXRvc19kZV9hbGV4ICU+JSBndCgpICU+JSB0ZXh0X3RyYW5zZm9ybSggbG9jYXRpb25zID0gY2VsbHNfYm9keShjb2x1bW5zID0gYyhmb3Rvc19uYWNfMjAyMSkpLCBmbiA9IGZ1bmN0aW9uKHgpIHtndDo6bG9jYWxfaW1hZ2UoeCwgaGVpZ2h0ID0gNTApfSkgJT4lIHRleHRfdHJhbnNmb3JtKCBsb2NhdGlvbnMgPSBjZWxsc19ib2R5KGNvbHVtbnMgPSBjKGZvdG9zX3BhaXNfMjAyMSkpLCBmbiA9IGZ1bmN0aW9uKHgpIHtndDo6bG9jYWxfaW1hZ2UoeCwgaGVpZ2h0ID0gNTApfSkgJT4lIHRleHRfdHJhbnNmb3JtKCBsb2NhdGlvbnMgPSBjZWxsc19ib2R5KGNvbHVtbnMgPSBjKGZvdG9zX2VzY18yMDIxKSksIGZuID0gZnVuY3Rpb24oeCkge2d0Ojpsb2NhbF9pbWFnZSh4LCBoZWlnaHQgPSA1MCl9KSAlPiUgdGFiX2hlYWRlcih0aXRsZSA9IG1kKCIqKlBpbG90b3MgMjAyMSoqIiksIHN1YnRpdGxlID0gbWQoIlBhcnJpbGxhIikpICU+JSAgIGNvbHNfbGFiZWwoDQogICAjIGRyaXZlclJlZiA9IGh0bWwoIiIpLA0KICAgICNudW1lcm9fY2FycmVyYXMgPSBodG1sKCJOwrogY2FycmVyYXMiKSwNCiAgICAjbl92aWN0b3JpYXMgPSBodG1sKCJOwrogdmljdG9yaWFzIiksDQogICAgI2ZvdG9zX2VzcF9pbmcgPSBodG1sKCJQYcOtcyIpLA0KICAgICNmb3Rvc19BTE9fdnNfSEFNID0gaHRtbCgiIikpICU+JSAgDQogICN0YWJfb3B0aW9ucyh0YWJsZS5iYWNrZ3JvdW5kLmNvbG9yID0gImdyYXkxMyIsICAgdGFibGUuZm9udC5jb2xvci5saWdodCA9ICJjeWFuIikgJT4lIA0KICAjY29sc19hbGlnbihhbGlnbiA9ICJjZW50ZXIiLA0KICAjY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKSkNCg0KI2Fsb192c19oYW1fdGFibGENCmBgYA0KDQoNCiMjIEVzcGHDsW9sZXMgcG9yIGxhIEYxDQoNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KI3BpbG90b3MgZXNwYcOxb2xlcw0KDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQoNCnBpbG90b3NfZXNwIDwtIHBpbG90b3MgJT4lIGZpbHRlcihuYXRpb25hbGl0eSA9PSAiU3BhbmlzaCIpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHkpIA0KDQoNCiNtYXMgdmljdG9yaWFzIGRlIHBpbG90b3MgZXNwYcOxb2xlcw0KbWFzX3ZpY3Rvcmlhc19lc3AgPC0gZnVsbF9qb2luKHZpY3Rvcmlhc19jb25fbm9tYnJlLCBwaWxvdG9zLCBjICgiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSBmaWx0ZXIobmF0aW9uYWxpdHkueCA9PSAiU3BhbmlzaCIpICU+JSAgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYueCwgbl92aWN0b3JpYXMsIG5hdGlvbmFsaXR5LngpIA0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpgYGANCg0KDQoNCiMjIEVzY3VkZXLDrWFzIGNhbXBlb25hcyB7LnRhYnNldH0NCg0KIyMjIFBvciB0YW1hw7FvDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFLCBpbmNsdWRlID0gRkFMU0V9DQojZGF0b3MgZGUgZXNjdWRlcmlhcyBwYSBxdWllbiBxdWllcmEgaGFjZXIgYWxnbw0KZXNjdWRlcmlhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3JzLmNzdiIpDQplc2N1ZGVyaWFzMiA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3Jfc3RhbmRpbmdzLmNzdiIpDQpyZXN1bHRfZXNjdWRlcmlhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3JfcmVzdWx0cy5jc3YiKQ0KDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQpjYXJyZXJhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmFjZXMuY3N2IikNCiNlc2N1ZGVyaWFzZXNwIDwtIGVzY3VkZXJpYXMgJT4lIGZpbHRlcihuYXRpb25hbGl0eSA9PSAiU3BhbmlzaCIpICNlc2N1ZGVyaWFzIGVzcGHDsW9sYXMNCg0KY2FtcGVvbmVzX2VzYyA8LSBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgYygiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSBmdWxsX2pvaW4oLiwgY2FycmVyYXMsIGMoInJhY2VJZCIgPSAicmFjZUlkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHksIGNvbnN0cnVjdG9ySWQsIHBvaW50cywgeWVhciwgcm91bmQpICU+JSBmdWxsX2pvaW4oLiwgZXNjdWRlcmlhcywgYygiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hdGlvbmFsaXR5LngsIGNvbnN0cnVjdG9ySWQsIHBvaW50cywgeWVhciwgbmFtZSwgcm91bmQpICU+JSAgZ3JvdXBfYnkoeWVhciwgZHJpdmVyUmVmKSAlPiUgIG11dGF0ZShwdW50b3NfdG90YWxlcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieSh5ZWFyKSAlPiUgc2xpY2VfbWF4KHB1bnRvc190b3RhbGVzLCBuPTEpICU+JSBzZWxlY3QobmFtZSwgZHJpdmVyUmVmKSAlPiUgZ3JvdXBfYnkobmFtZSwgZHJpdmVyUmVmKSAlPiUgbXV0YXRlKHRvdGFsX2NhbXAgPSBzdW0oIE5OID0gbigpKSkgJT4lIGFycmFuZ2UobmFtZSkgDQoNCg0KY2FtcGVvbmVzX2VzYyA8LSBjYW1wZW9uZXNfZXNjWyEoY2FtcGVvbmVzX2VzYyRkcml2ZXJSZWYgPT0gJ21heF92ZXJzdGFwcGVuJyksXSANCg0KbGlicmFyeSh0cmVlbWFwKQ0KbGlicmFyeShkM3RyZWVSKQ0KDQoNCiMgYmFzaWMgdHJlZW1hcA0KZ2dfZXNjX2NhbXBlb25lcyA8LSB0cmVlbWFwKGNhbXBlb25lc19lc2MsDQogICAgICAgICAgICBpbmRleD1jKCJuYW1lIiwiZHJpdmVyUmVmIiksDQogICAgICAgICAgICB2U2l6ZT0idG90YWxfY2FtcCIsDQogICAgICAgICAgICB0eXBlPSJpbmRleCIsDQogICAgICAgICAgICB2Q29sb3IgPSAibmFtZSIsDQogICAgICAgICAgICBmb250c2l6ZS5sYWJlbHM9YygxNSwyMCksDQogICAgICAgICAgICBiZy5sYWJlbHM9YygidHJhbnNwYXJlbnQiKSwNCiAgICAgICAgICAgIHBhbGV0dGUgPSAiU2V0MiIsDQogICAgICAgICAgICBhbGlnbi5sYWJlbHM9bGlzdCgNCiAgICAgICAgICAgICAgYygiY2VudGVyIiwgImNlbnRlciIpLCANCiAgICAgICAgICAgICAgYygiY2VudGVyIiwgImJvdHRvbSIpKSwNCiAgICAgICAgICAgIHRpdGxlID0gIkVzY3VkZXLDrWFzIGNvbiBtw6FzIGNhbXBlb25lcyIsDQogICAgICAgICAgICB0aXRsZS5sZWdlbmQgPSAiRXNjdWRlcsOtYXMiKSAgIA0KDQpgYGANCg0KDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KI2RhdG9zIGRlIGVzY3VkZXJpYXMgcGEgcXVpZW4gcXVpZXJhIGhhY2VyIGFsZ28NCiNlc2N1ZGVyaWFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9jb25zdHJ1Y3RvcnMuY3N2IikNCiNlc2N1ZGVyaWFzMiA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3Jfc3RhbmRpbmdzLmNzdiIpDQojcmVzdWx0X2VzY3VkZXJpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9yX3Jlc3VsdHMuY3N2IikNCg0KI3BpbG90b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2RyaXZlcnMuY3N2IikNCiNyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQojY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQojZXNjdWRlcmlhc2VzcCA8LSBlc2N1ZGVyaWFzICU+JSBmaWx0ZXIobmF0aW9uYWxpdHkgPT0gIlNwYW5pc2giKSAjZXNjdWRlcmlhcyBlc3Bhw7FvbGFzDQoNCiNjYW1wZW9uZXNfZXNjIDwtIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCBjKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIGZ1bGxfam9pbiguLCBjYXJyZXJhcywgYygicmFjZUlkIiA9ICJyYWNlSWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYXRpb25hbGl0eSwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCByb3VuZCkgJT4lIGZ1bGxfam9pbiguLCBlc2N1ZGVyaWFzLCBjKCJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHkueCwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCBuYW1lLCByb3VuZCkgJT4lICBncm91cF9ieSh5ZWFyLCBkcml2ZXJSZWYpICU+JSAgbXV0YXRlKHB1bnRvc190b3RhbGVzID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHllYXIpICU+JSBzbGljZV9tYXgocHVudG9zX3RvdGFsZXMsIG49MSkgJT4lIHNlbGVjdChuYW1lLCBkcml2ZXJSZWYpICU+JSBncm91cF9ieShuYW1lLCBkcml2ZXJSZWYpICU+JSBtdXRhdGUodG90YWxfY2FtcCA9IHN1bSggTk4gPSBuKCkpKSAlPiUgYXJyYW5nZShuYW1lKSANCg0KDQojY2FtcGVvbmVzX2VzYyA8LSBjYW1wZW9uZXNfZXNjWyEoY2FtcGVvbmVzX2VzYyRkcml2ZXJSZWYgPT0gJ21heF92ZXJzdGFwcGVuJyksXSANCg0KI2xpYnJhcnkodHJlZW1hcCkNCiNsaWJyYXJ5KGQzdHJlZVIpDQoNCg0KIyBiYXNpYyB0cmVlbWFwDQojZ2dfZXNjX2NhbXBlb25lcyA8LSB0cmVlbWFwKGNhbXBlb25lc19lc2MsDQogICAgICAgICAgICAjaW5kZXg9YygibmFtZSIsImRyaXZlclJlZiIpLA0KICAgICAgICAgICAgI3ZTaXplPSJ0b3RhbF9jYW1wIiwNCiAgICAgICAgICAgICN0eXBlPSJpbmRleCIsDQogICAgICAgICAgICAjdkNvbG9yID0gIm5hbWUiLA0KICAgICAgICAgICAgI2ZvbnRzaXplLmxhYmVscz1jKDI1LDE3KSwNCiAgICAgICAgICAgICNiZy5sYWJlbHM9YygidHJhbnNwYXJlbnQiKSwNCiAgICAgICAgICAgICNwYWxldHRlID0gIlNldDIiLA0KICAgICAgICAgICAgI2FsaWduLmxhYmVscz1saXN0KA0KICAgICAgICAgICAgICAjYygiY2VudGVyIiwgImNlbnRlciIpLCANCiAgICAgICAgICAgICAgI2MoImNlbnRlciIsICJib3R0b20iKSksDQogICAgICAgICAgICAjdGl0bGUgPSAiRXNjdWRlcsOtYXMgY29uIG3DoXMgY2FtcGVvbmVzIiwNCiAgICAgICAgICAgICN0aXRsZS5sZWdlbmQgPSAiRXNjdWRlcsOtYXMiKSAgIA0KDQpgYGANCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCg0KaW50ZXJfY2FtcCA8LSBkM3RyZWUyKGdnX2VzY19jYW1wZW9uZXMgLCAgcm9vdG5hbWUgPSAiRXNjdWRlcsOtYXMgeSBDYW1wZW9uZXMiKQ0KaW50ZXJfY2FtcA0KDQoNCmBgYA0KDQojIyMgRW4gdmFsb3JlcyBhYnNvbHV0b3MNCg0KYGBge3J9DQoNCmNhbXBlb25lc19lc2MgPC0gZnVsbF9qb2luKHBpbG90b3MsIHJlc3VsdGFkb3MsIGMoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgZnVsbF9qb2luKC4sIGNhcnJlcmFzLCBjKCJyYWNlSWQiID0gInJhY2VJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hdGlvbmFsaXR5LCBjb25zdHJ1Y3RvcklkLCBwb2ludHMsIHllYXIsIHJvdW5kKSAlPiUgZnVsbF9qb2luKC4sIGVzY3VkZXJpYXMsIGMoImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYXRpb25hbGl0eS54LCBjb25zdHJ1Y3RvcklkLCBwb2ludHMsIHllYXIsIG5hbWUsIHJvdW5kKSAlPiUgIGdyb3VwX2J5KHllYXIsIGRyaXZlclJlZikgJT4lICBtdXRhdGUocHVudG9zX3RvdGFsZXMgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHNsaWNlX21heChwdW50b3NfdG90YWxlcywgbj0xKSAlPiUgdW5ncm91cCgpICU+JSBjb3VudChuYW1lKSAlPiUgYXJyYW5nZShkZXNjKG4pKQ0KDQoNCmdncGxvdChjYW1wZW9uZXNfZXNjLCBhZXMoeCA9IHJlb3JkZXIobmFtZSxuKSx5ID0gbikpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KDQoNCiMgQWxvbnNvIChlbCBuYW5vKQ0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCiNhbG9uc28gdnMgY29tcGHDsWVyb3MgZGUgZXF1aXBvDQoNCnBpbG90b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2RyaXZlcnMuY3N2IikNCnJlc3VsdGFkb3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3Jlc3VsdHMuY3N2IikNCmVzY3VkZXJpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9ycy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQoNCmFsb3ZzYWxsIDwtIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCBjICgiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSAgc2VsZWN0KGRyaXZlclJlZiwgcmVzdWx0SWQsIHJhY2VJZCwgY29uc3RydWN0b3JJZCwgcG9zaXRpb24sIHBvaW50cykgJT4lIGZ1bGxfam9pbiguLCBlc2N1ZGVyaWFzLCBjICgiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIpKSAlPiUgc2VsZWN0KGRyaXZlclJlZiwgcmVzdWx0SWQsIHJhY2VJZCwgY29uc3RydWN0b3JJZCwgcG9zaXRpb24sIHBvc2l0aW9uLCBwb2ludHMsIG5hbWUpICU+JSBmdWxsX2pvaW4oLiwgY2FycmVyYXMsIGMgKCJyYWNlSWQiID0gInJhY2VJZCIpKSAlPiUgc2VsZWN0KGRyaXZlclJlZiwgcmVzdWx0SWQsIHJhY2VJZCwgY29uc3RydWN0b3JJZCwgcG9zaXRpb24sIHBvc2l0aW9uLCBwb2ludHMsIG5hbWUueCwgeWVhciwgcm91bmQpDQoNCiNhbG9fdnNfbWFycXVlcyA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgPT0gMjAwMSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgIm1hcnF1ZXMiKSwgcm91bmQgPD0gMTQpDQoNCmFsb192c190cnVsbGkgPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDAzLCAyMDA0KSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgInRydWxsaSIpKSAlPiUgc2xpY2UoMToxNSwgMTc6NjcpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfZmlzaWNoZWxsYSA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgJWluJSBjKDIwMDUsIDIwMDYpLCBkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAiZmlzaWNoZWxsYSIpKSAgJT4lIGdyb3VwX2J5KGRyaXZlclJlZiwgeWVhcikgJT4lIG11dGF0ZShwdW50b3NfYWN1bXVsYWRvcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpDQoNCmFsb192c19oYW1pbHRvbiA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgJWluJSBjKDIwMDcpICxkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAiaGFtaWx0b24iKSkgICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfcGlxdWV0IDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAwOCwgMjAwOSksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJwaXF1ZXRfanIiKSkgJT4lIHNsaWNlKDE6MjgsIDM2OjYzKSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkNCg0KI2Fsb192c19ncm9zamVhbiA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgPT0gMjAwOSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgImdyb3NqZWFuIiksIHJvdW5kID49IDExKQ0KDQphbG9fdnNfbWFzc2EgPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDEwLCAyMDExLCAyMDEzKSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgIm1hc3NhIikpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfcmFpa2tvbmVuIDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE0LCBkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAicmFpa2tvbmVuIikpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfYnV0dG9uIDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAxNSwgMjAxNiksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJidXR0b24iKSkgJT4lIGdyb3VwX2J5KGRyaXZlclJlZiwgeWVhcikgJT4lIG11dGF0ZShwdW50b3NfYWN1bXVsYWRvcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpDQoNCmFsb192c192YW5kb29ybmUgPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDE3LCAyMDE4KSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgInZhbmRvb3JuZSIpKSAlPiUgc2xpY2UoMTo0NSwgNDc6ODEpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSklPiUgdW5ncm91cCgpIA0KDQphbG9fdnNfb2NvbiA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgIm9jb24iKSkgJT4lIGdyb3VwX2J5KGRyaXZlclJlZiwgeWVhcikgJT4lIG11dGF0ZShwdW50b3NfYWN1bXVsYWRvcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpDQoNCkFMT19WU19BTEwgPC0gZnVsbF9qb2luKGFsb192c190cnVsbGksIGFsb192c19maXNpY2hlbGxhLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIgLCAicHVudG9zX2FjdW11bGFkb3MiID0gInB1bnRvc19hY3VtdWxhZG9zIikpICU+JSANCiAgZnVsbF9qb2luKC4sIGFsb192c19oYW1pbHRvbiwgYygiZHJpdmVyUmVmIj0gImRyaXZlclJlZiIsICJyZXN1bHRJZCIgPSAicmVzdWx0SWQiLCAicmFjZUlkIiA9ICJyYWNlSWQiLCAiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIsICJwb3NpdGlvbiIgPSAicG9zaXRpb24iLCAicG9pbnRzIiA9ICJwb2ludHMiLCAibmFtZS54IiA9ICJuYW1lLngiLCAieWVhciIgPSAieWVhciIsICJyb3VuZCIgPSAicm91bmQiLCAicHVudG9zX2FjdW11bGFkb3MiID0gInB1bnRvc19hY3VtdWxhZG9zIikpICU+JSANCiAgZnVsbF9qb2luKC4sIGFsb192c19waXF1ZXQsIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAlPiUgDQogIGZ1bGxfam9pbiguLCBhbG9fdnNfbWFzc2EsIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAgJT4lIA0KICBmdWxsX2pvaW4oLiwgYWxvX3ZzX3JhaWtrb25lbiwgYygiZHJpdmVyUmVmIj0gImRyaXZlclJlZiIsICJyZXN1bHRJZCIgPSAicmVzdWx0SWQiLCAicmFjZUlkIiA9ICJyYWNlSWQiLCAiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIsICJwb3NpdGlvbiIgPSAicG9zaXRpb24iLCAicG9pbnRzIiA9ICJwb2ludHMiLCAibmFtZS54IiA9ICJuYW1lLngiLCAieWVhciIgPSAieWVhciIsICJyb3VuZCIgPSAicm91bmQiLCAicHVudG9zX2FjdW11bGFkb3MiID0gInB1bnRvc19hY3VtdWxhZG9zIikpICU+JSANCiAgZnVsbF9qb2luKC4sIGFsb192c19idXR0b24sIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAlPiUgDQogIGZ1bGxfam9pbiguLCBhbG9fdnNfdmFuZG9vcm5lLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIsICJwdW50b3NfYWN1bXVsYWRvcyIgPSAicHVudG9zX2FjdW11bGFkb3MiKSkgJT4lIA0KICBmdWxsX2pvaW4oLiwgYWxvX3ZzX29jb24sIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKQ0KDQpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJBTE9fVlNfQUxMIiwgIm5fY2FycmVyYXNfbm9tIiwgInZpY3Rvcmlhc19jb25fbm9tYnJlIikNCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQ0KDQoNCmdjKCkgI2luc3RydWNjaW9uIHBhcmEgcXVlIGNhcmd1ZSBlbCBncmFmaWNvLCBhbCBzZXIgdGFuIGNvbXBsZWpvIGRhIGVycm9yIGRlIG5vIHPDqSBxdcOpIHBlcm8gY29uIGVzdG8gZnVuY2lvbmENCmdnYWxvX3ZzX2FsbCA8LSBnZ3Bsb3QoZGF0YSA9IEFMT19WU19BTEwsIGFlcyhyb3VuZCwgcHVudG9zX2FjdW11bGFkb3MsIGNvbG9yID0gZHJpdmVyUmVmKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBsYWJzKHRpdGxlID0gIkFsb25zbyBjb250cmEgZWwgbXVuZG8iLA0KICAgICAgIHN1YnRpdGxlID0gImxlIGRhcyB1biBjYXJ0b24gY29uIHJ1ZWRhcyB5IGHDum4gdGUgc2FjYSBwdW50b3MiLA0KICAgICAgIHkgPSAiUHVudG9zIiwgeCA9ICIiKSArIGZhY2V0X3dyYXAoIH4geWVhcikgKyB0cmFuc2l0aW9uX3JldmVhbChyb3VuZCkNCg0KI2dnYWxvX3ZzX2FsbA0KDQpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJ2aWN0b3JpYXNfY29uX25vbWJyZSIsICJuX2NhcnJlcmFzX25vbSIsICJtYXNfdmljdG9yaWFzIikNCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQ0KYGBgDQoNCiMgQ2FtcGVvbmVzIGRlbCBtdW5kbw0KDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQplc2N1ZGVyaWFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9jb25zdHJ1Y3RvcnMuY3N2IikNCmNhcnJlcmFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yYWNlcy5jc3YiKQ0KDQoNCg0KY2FtcGVvbmVzIDwtIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCBjKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIGZ1bGxfam9pbiguLCBjYXJyZXJhcywgYygicmFjZUlkIiA9ICJyYWNlSWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYXRpb25hbGl0eSwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCByb3VuZCkgJT4lIGZ1bGxfam9pbiguLCBlc2N1ZGVyaWFzLCBjKCJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHkueCwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCBuYW1lLCByb3VuZCkgJT4lICBncm91cF9ieSh5ZWFyLCBkcml2ZXJSZWYpICU+JSAgbXV0YXRlKHB1bnRvc190b3RhbGVzID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHllYXIpICU+JSBzbGljZV9tYXgocHVudG9zX3RvdGFsZXMsIG49MSkgJT4lIHVuZ3JvdXAoKSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmKSU+JSBtdXRhdGUodG90YWxfY2FtcGVvbmF0b3MgPSBzdW0oTk4gPSBuKCkpKSAlPiUgZGlzdGluY3QoZHJpdmVyUmVmLCBuYXRpb25hbGl0eS54LCB0b3RhbF9jYW1wZW9uYXRvcykgJT4lIGFycmFuZ2UobmF0aW9uYWxpdHkueCwgdG90YWxfY2FtcGVvbmF0b3MpDQogIA0KY2FtcGVvbmVzIDwtIGNhbXBlb25lc1shKGNhbXBlb25lcyRkcml2ZXJSZWYgPT0gJ21heCB2ZXJzdGFwcGVuJyksXQ0KaWQgPC0gcm93bmFtZXMoY2FtcGVvbmVzKQ0KY2FtcGVvbmVzIDwtIGNiaW5kKGlkPWlkLCBjYW1wZW9uZXMpDQpjYW1wZW9uZXNbLCBjKDEpXSA8LSBzYXBwbHkoY2FtcGVvbmVzWywgYygxKV0sIGFzLm51bWVyaWMpDQoNCg0KDQoNCmxhYmVsX2NhbXBlb25lcyA8LSBjYW1wZW9uZXMNCm51bWJlcl9vZl9iYXIgPC0gbnJvdyhsYWJlbF9jYW1wZW9uZXMpDQoNCmFuZ2xlIDwtIDkwIC0gMzYwICogKGxhYmVsX2NhbXBlb25lcyRpZC0wLjUpIC9udW1iZXJfb2ZfYmFyICAgICAjIEkgc3Vic3RyYWN0IDAuNSBiZWNhdXNlIHRoZSBsZXR0ZXIgbXVzdCBoYXZlIHRoZSBhbmdsZSBvZiB0aGUgY2VudGVyIG9mIHRoZSBiYXJzLiBOb3QgZXh0cmVtZSByaWdodCgxKSBvciBleHRyZW1lIGxlZnQgKDApDQpsYWJlbF9jYW1wZW9uZXMkaGp1c3QgPC0gaWZlbHNlKCBhbmdsZSA8IC05MCwgMSwgMCkNCmxhYmVsX2NhbXBlb25lcyRhbmdsZSA8LSBpZmVsc2UoYW5nbGUgPCAtOTAsIGFuZ2xlKzE4MCwgYW5nbGUpDQoNCg0KDQpiYXNlX2NhbXBlb25lcyA8LSBjYW1wZW9uZXMgJT4lIA0KICBncm91cF9ieShuYXRpb25hbGl0eS54KSAlPiUgDQogIHN1bW1hcmlzZShzdGFydD1taW4oaWQpLCBlbmQ9bWF4KGlkKSkgJT4lIA0KICByb3d3aXNlKCkgJT4lIA0KICBtdXRhdGUodGl0bGU9bWVhbihjKHN0YXJ0LCBlbmQpKSkNCg0KZ3JpZF9jYW1wZW9uZXMgPC0gYmFzZV9jYW1wZW9uZXMNCmdyaWRfY2FtcGVvbmVzJGVuZCA8LSBncmlkX2NhbXBlb25lcyRlbmRbIGMoIG5yb3coZ3JpZF9jYW1wZW9uZXMpLCAxOm5yb3coZ3JpZF9jYW1wZW9uZXMpLTEpXSArIDENCmdyaWRfY2FtcGVvbmVzJHN0YXJ0IDwtIGdyaWRfY2FtcGVvbmVzJHN0YXJ0IC0gMQ0KZ3JpZF9jYW1wZW9uZXMgPC0gZ3JpZF9jYW1wZW9uZXNbLTEsXQ0KDQpwIDwtIGdncGxvdChjYW1wZW9uZXMsIGFlcyh4PWFzLmZhY3Rvcih5ZWFyKSwgeT10b3RhbF9jYW1wZW9uYXRvcywgZmlsbD1uYXRpb25hbGl0eS54LCBjb2xvciA9IG5hdGlvbmFsaXR5LngpKSArIGdlb21fYmFyKGFlcyh4PWFzLmZhY3RvcihpZCksIHk9dG90YWxfY2FtcGVvbmF0b3MsIGZpbGw9bmF0aW9uYWxpdHkueCksIHN0YXQ9ImlkZW50aXR5IiwgYWxwaGE9MC41KSArDQogIA0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAwLCB5ID0gOCwgeGVuZCA9IDMxLCB5ZW5kID0gOCksIGNvbG91ciA9ICJncmV5IiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAwLCB5ID0gNiwgeGVuZCA9IDMxLCB5ZW5kID0gNiksIGNvbG91ciA9ICJncmV5IiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAwLCB5ID0gNCwgeGVuZCA9IDMxLCB5ZW5kID0gNCksIGNvbG91ciA9ICJncmV5IiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAwLCB5ID0gMiwgeGVuZCA9IDMxLCB5ZW5kID0gMiksIGNvbG91ciA9ICJncmV5IiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICANCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gcmVwKG1heChjYW1wZW9uZXMkaWQpLDQpLCB5ID0gYygyLCA0LCA2LCA4KSwgbGFiZWwgPSBjKCIyIiwgIjQiLCAiNiIsICI4IikgLCBjb2xvcj0id2hpdGUiLCBzaXplPTMgLCBhbmdsZT0wLCBmb250ZmFjZT0iYm9sZCIsIGhqdXN0PTEpICsNCiAgDQogICBnZW9tX2JhcihhZXMoeD1hcy5mYWN0b3IoaWQpLCB5PXRvdGFsX2NhbXBlb25hdG9zLCBmaWxsPW5hdGlvbmFsaXR5LngpLCBzdGF0PSJpZGVudGl0eSIsIGFscGhhPTAuNSkgKw0KICB5bGltKC0xMCwyMSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90Lm1hcmdpbiA9IHVuaXQocmVwKC0xLDQpLCAiY20iKSApICsNCiAgY29vcmRfcG9sYXIoKSArIA0KICBnZW9tX3RleHQoZGF0YT1sYWJlbF9jYW1wZW9uZXMsIGFlcyh4PWlkLCB5PTEwLCBsYWJlbD1kcml2ZXJSZWYsIGhqdXN0PWhqdXN0KSwgY29sb3I9IndoaXRlIiwgZm9udGZhY2U9ImJvbGQiLGFscGhhPTAuNiwgc2l6ZT0zLjUsIGFuZ2xlPSBsYWJlbF9jYW1wZW9uZXMkYW5nbGUsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQoNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMC43MCwgeSA9IC0xLCB4ZW5kID0gMi40NSwgeWVuZCA9IC0xKSwgY29sb3VyID0gIndoaXRlIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMi42LCB5ID0gLTEsIHhlbmQgPSAzLjU1LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAid2hpdGUiLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDMuNjUsIHkgPSAtMSwgeGVuZCA9IDUuNDUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJ3aGl0ZSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gNS41NSwgeSA9IC0xLCB4ZW5kID0gNy4zNSwgeWVuZCA9IC0xKSwgY29sb3VyID0gIndoaXRlIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSA3LjUsIHkgPSAtMSwgeGVuZCA9IDEwLjUwLCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAid2hpdGUiLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDEwLjcsIHkgPSAtMSwgeGVuZCA9IDE5LjIwLCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAid2hpdGUiLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDE5LjQsIHkgPSAtMSwgeGVuZCA9IDIwLjMsIHllbmQgPSAtMSksIGNvbG91ciA9ICJ3aGl0ZSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMjAuNDUsIHkgPSAtMSwgeGVuZCA9IDIzLjQsIHllbmQgPSAtMSksIGNvbG91ciA9ICJ3aGl0ZSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsgDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDIzLjY1LCB5ID0gLTEsIHhlbmQgPSAyNC4zNSwgeWVuZCA9IC0xKSwgY29sb3VyID0gIndoaXRlIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKyANCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMjQuNjAsIHkgPSAtMSwgeGVuZCA9IDI3LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAid2hpdGUiLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDI3LjIsIHkgPSAtMSwgeGVuZCA9IDI5LjUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJ3aGl0ZSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMjkuNywgeSA9IC0xLCB4ZW5kID0gMzAuNSwgeWVuZCA9IC0xKSwgY29sb3VyID0gIndoaXRlIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKyANCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMzAuNywgeSA9IC0xLCB4ZW5kID0gMzEuNSwgeWVuZCA9IC0xKSwgY29sb3VyID0gIndoaXRlIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAzMS43LCB5ID0gLTEsIHhlbmQgPSAzMi41LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAid2hpdGUiLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dCggY29sb3VyID0gIndoaXRlIiksIA0KICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXkxMyIsIGNvbG91ciA9ICJncmF5MTMiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTMiICwgY29sb3VyID0gImdyYXkxMyIpLA0KICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXkxMyIgLCBjb2xvdXIgPSAiZ3JheTEzIikpDQogIA0KDQoNCnANCiAgIA0KDQpgYGANCg0KDQojIFRlbXBsb3MgeSB0aWVtcG9zDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KDQpjYXJyZXJhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmFjZXMuY3N2IikNCmNpcmN1aXRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY2lyY3VpdHMuY3N2IikNCg0KY2FycmVyYXNfMjEgPC0gZnVsbF9qb2luKGNhcnJlcmFzLGNpcmN1aXRvcywgYygiY2lyY3VpdElkIiA9ICJjaXJjdWl0SWQiKSkgJT4lDQogIGZpbHRlcih5ZWFyPT0iMjAyMCIpICU+JQ0KICBzZWxlY3Qocm91bmQsIG5hbWUueCwgbmFtZS55LCBkYXRlLCBsb2NhdGlvbixjb3VudHJ5LCBsYXQsIGxuZywgYWx0KSAlPiUNCiAgYXJyYW5nZShyb3VuZCkgJT4lIA0KICBtdXRhdGUocm91bmQyID0gcm91bmQpIA0KDQpjYXJyZXJhc18yMV92MiA8LSBjYXJyZXJhc18yMVssIGMoMSwgNCwgMTAsIDIsIDMsIDUsIDYsIDcsIDgsIDkpXQ0KY2FycmVyYXNfMjFfdjIgPC0gY2FycmVyYXNfMjFfdjIlPiUgIHVuaXRlKC4gLHZhcmlhYmxlcywgYygxLCA1LCA3KSwgc2VwID0gIjsgIikNCg0KI3BydWViYXMgcGFyYSBtYXBhcywgbm8gZWplY3V0YXIgZGUgbW9tZW50bw0KI2xpYnJhcnkod2lkZ2V0ZnJhbWUpDQojbGlicmFyeShsZWFmbGV0KQ0KI2wgPC0gbGVhZmxldCgpICU+JSBzZXRWaWV3KGxhdCA9IDQ1LjYxNTYwLCBsbmcgPSA5LjI4MTExMCwgem9vbT0xKQ0KI2ZyYW1lV2lkZ2V0KGwpIA0KDQojbWFwYUNpdWRhZHlQdWVibG9tYXlvcmluY2lBY3UgPC0gbGVhZmxldCgpICU+JQ0KICMgc2V0VmlldyhsbmcgPSAtMC4yNDM1OTEsIGxhdCA9IDM4LjgyMSwgem9vbSA9IDcpICU+JSANCiAgI2FkZE1hcmtlcnMobG5nID0gLTAuMjQzNTkxLCBsYXQgPSAzOC44MjEgLCBwb3B1cCA9ICJWYWxsIGRlIEdhbGxpbmVyYSIpJT4lDQogICNzZXRWaWV3KGxuZyA9IC0wLjQxODU5OCwgbGF0ID0gNDAuMjAxMSwgem9vbSA9IDcpICU+JSANCiAgI2FkZE1hcmtlcnMobG5nID0gLTAuNDE4NTk4LCBsYXQgPSA0MC4yMDExICwgcG9wdXAgPSAiVmlsbGFoZXJtb3NhIGRlbCByaW8iKSAlPiUgYWRkVGlsZXMoKQ0KI21hcGFDaXVkYWR5UHVlYmxvbWF5b3JpbmNpQWN1DQoNCiNNQVBBIERFTCBNVU5ETyBERSBMQSBPU1RJQSBOTyBUT0NBUiwgcG9uZ28gY29tbyBjb21lbnRhcmlvIHBhcmEgcXVlIG5vIHRhcmRlIHRhbnRvIGFsIGtuaXRlYXINCg0KDQpnbG9ib19jaXJjIDwtY3JlYXRlX2dsb2JlKCkgJT4lIGdsb2JlX3Bvdig0NS42MTU2MCwgOS4yODExMTApICU+JSBnbG9iZV9iYXJzKGNvb3JkcyhsYXQsIGxuZywgbGFiZWwgID0gdmFyaWFibGVzLCBjb2xvciA9IHJvdW5kMiksIGRhdGEgPSBjYXJyZXJhc18yMV92MikgICU+JSBzY2FsZV9iYXJzX2NvbG9yKCkNCiNnbG9ib19jaXJjDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIiwgIm1hc192aWN0b3JpYXMiKQ0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBvYmpldG9zX25vX2JvcnJhcl0pDQpgYGANCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCmNpcmN1aXRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY2lyY3VpdHMuY3N2IikNCnRpZW1wb3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2xhcF90aW1lcy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQoNCmNpcmN1aXRvc19ncCA8LSBmdWxsX2pvaW4oY2FycmVyYXMsIGNpcmN1aXRvcywgYygiY2lyY3VpdElkIiA9ICJjaXJjdWl0SWQiKSkgI2Fzb2NpbyBsYXMgY2FycmVyYXMgYSBzdSBjaXJjdWl0bw0KdGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEgPC0gZnVsbF9qb2luKGNpcmN1aXRvc19ncCwgdGllbXBvcywgYyAoInJhY2VJZCIgPSAicmFjZUlkIikpICN0aWVtcG8gZGUgbGFzIHZ1ZWx0YXMgcG9yIGNhZGEgY2FycmVyYQ0KDQp0aWVtcG9zdnVlbHRhX3hfY2FycmVyYSA8LSBmdWxsX2pvaW4odGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEsIHBpbG90b3MsIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lICBzZWxlY3QoY2lyY3VpdElkLCBuYW1lLnksIGRyaXZlcklkLCBkcml2ZXJSZWYsIHRpbWUueSwgbGFwLHBvc2l0aW9uLCB5ZWFyLCBjb3VudHJ5KSAjZnVzaW9ubyBjb24gZWwgZGYgZGUgcGlsb3RvcyBwYXJhIGFzb2NpYXIgY2FkYSB2dWVsdGEgYWwgbm9tYnJlIGRlbCBwaWxvdG8gcXVlIGxhIGhpem8NCg0KDQojY2FsY3VsbyBlbCByZWNvcmQgZGUgY2FkYSBjaXJjdWl0bywgZmlsdHJhbmRvIGVsIG1pbmltbyBkZSBsb3MgdGllbXBvcyBlbiBjYWRhIGNpcmN1aXRvDQpyZWNvcmRfZGVfY2lyY3VpdG8gPC0gdGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEgJT4lIGdyb3VwX2J5KG5hbWUueSkgJT4lIHNsaWNlX21pbih0aW1lLnksIG49MSkNCg0KI251bWVybyBkZSByZWNvcmRzIGRlIGNpcmN1aXRvIHF1ZSB0aWVuZSBjYWRhIHBpbG90bw0KcmVjb3JkX3hfcGlsb3RvIDwtIHJlY29yZF9kZV9jaXJjdWl0byAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUobnVtZXJvX3JlY29yZHMgPSBzdW0obigpKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBudW1lcm9fcmVjb3JkcykgJT4lIGRpc3RpbmN0KGRyaXZlclJlZiwgbnVtZXJvX3JlY29yZHMpICU+JSBhcnJhbmdlKGRlc2MobnVtZXJvX3JlY29yZHMpKQ0KDQoNCiMgbWV0ZXIgbWFwYSBkZWwgbXVuZG8gY29uIGxhIHViaWNhY2lvbiBkZSBsb3MgY2lyY3VpdG9zIGVuIGxhIHRlbXBvcmFkYSAyMDIxDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIiwgIm1hc192aWN0b3JpYXMiKQ0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBvYmpldG9zX25vX2JvcnJhcl0pDQoNCmBgYA0KDQojIENhcMOtdHVsbyBvc2N1cm8gZGVsIGRlcG9ydGUNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCg0KI2NyZW8gZGYgZGUgbXVlcnRlcyBkZSBmb3JtdWxhIDENCm11ZXJ0ZXNmMSA8LSBkYXRhLmZyYW1lKA0KICAib3JkZW4iID0gMTo0MiwNCiAgImRyaXZlclJlZiIgPSBjKCJDaGV0IE1pbGxlciIsICJDYXJsIFNjYXJhYm9yb3VnaCIsICJPbm9mcmUgTWFyaW1vbiIsICJNYW5ueSBBeXVsbyIsICJCaWxsIFZ1a292aWNoIiwgIkFsYmVydG8gQXNjYXJpIiwiRXVnZW5pbyBDYXN0ZWxsb3R0aSIsICJLZWl0aCBBbmRyZXdzIiwgIlBhdCBPJ0Nvbm5vciIsICJMdWlnaSBNdXNzbyIsICJQZXRlciBDb2xsaW5zIiwgIlN0dWFydCBMZXdpcy1FdmFucyIsICJKZXJyeSBVbnNlciIsICJCb2IgQ29ydG5lciIsICJJdm9yIEJ1ZWIiLCAiQ2hyaXMgQnJpc3RvdyIsICJBbGFuIFN0YWNleSIsICJHaXVsaW8gQ2FiaWFuY2EiLCAiV29sZmdhbmcgdm9uIFRyaXBzIiwgIkNhcmVsIEdvZGluIGRlIEJlYXVmb3J0IiwgIkpvaG4gVGF5bG9yIiwgIkxvcmVuem8gQmFuZGluaSIsICJCb2IgQW5kZXJzb24iLCAiSm8gU2NobGVzc2VyIiwgIkdlcmhhcmQgTWl0dGVyIiwgIlBpZXJzIENvdXJhZ2UiLCAiSm9jaGVuIFJpbmR0IiwgIkpvIFNpZmZlcnQiLCAiUm9nZXIgV2lsbGlhbXNvbiIsICJGcmFuw6dvaXMgQ2V2ZXJ0IiwgIlBldGVyIFJldnNvbiIsICJIZWxtdXRoIEtvaW5pZ2ciLCAiTWFyayBEb25vaHVlIiwgIlRvbSBQcnljZSIsICJSb25uaWUgUGV0ZXJzb24iLCAiUGF0cmljayBEZXBhaWxsZXIiLCAiR2lsbGVzIFZpbGxlbmV1dmUiLCAiUmljY2FyZG8gUGFsZXR0aSIsICJFbGlvIGRlIEFuZ2VsaXMiLCAiUm9sYW5kIFJhdHplbmJlcmdlciIsICJBeXJ0b24gU2VubmEiLCAiSnVsZXMgQmlhbmNoaSIpLA0KICAibmF0aW9uYWxpdHkiID0gYygiQW1lcmljYW4iLCAiQW1lcmljYW4iLCAiQXJnZW50aW5lIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkl0YWxpYW4iLCJJdGFsaWFuIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkl0YWxpYW4iLCAiQnJpdGlzaCIsICJCcml0aXNoIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkJyaXRpc2giLCAiQnJpdGlzaCIsICJCcml0aXNoIiwgIkl0YWxpYW4iLCAiR2VybWFuIiwgIkR1dGNoIiwgIkJyaXRpc2giLCAiSXRhbGlhbiIgLCAiQnJpdGlzaCIsICJGcmVuY2giLCAiR2VybWFuIiwgIkJyaXRpc2giLCAiQXVzdHJpYW4iLCAiU3dpc3MiLCAiQnJpdGlzaCIsICJGcmVuY2giLCAiQW1lcmljYW4iLCAiQXVzdHJpYW4iLCAiQW1lcmljYW4iLCAiQnJpdGlzaCIsICJTd2VkaXNoIiwgIkZyZW5jaCIsICJDYW5hZGlhbiIsICJJdGFsaWFuIiwgIkl0YWxpYW4iLCAiQXVzdHJpYW4iLCAiQnJhemlsaWFuIiwgIkZyZW5jaCIpLA0KICAiZmVjaGFfbXVlcnRlIiA9IGMoMTk1MywgMTk1MywgMTk1NCwgMTk1NSwgMTk1NSwgMTk1NSwgMTk1NywgMTk1NywgMTk1OCwxOTU4LDE5NTgsMTk1OCwxOTU5LDE5NTksMTk1OSwxOTYwLCAxOTYwLDE5NjEsMTk2MSwxOTY0LDE5NjYsIDE5NjcsIDE5NjcsMTk2OCwxOTY5LCAxOTcwLDE5NzAsMTk3MSwxOTczLDE5NzMsMTk3NCwxOTc0LDE5NzUsMTk3NywxOTc4LDE5ODAsMTk4MiwxOTgyLDE5ODYsMTk5NCwxOTk0LDIwMTQpKQ0KI25vIHBvbmdvIGxhcyBjb21pbGxhcyBlbiBsb3MgYcOxb3MgcGFyYSBxdWUgc2UgY3JlZW4gZGlyZWN0YW1lbnRlIGNvbW8gb2JzZXJ2YWNpb25lcyBudW1lcmljYXMNCg0KI2NyZW8gdW4gZGYgY29uIHRvZG9zIGxvcyBhw7FvcyBwYXJhIGx1ZWdvIGZ1c2lvbmFybG8sIHlhIHF1ZSBubyBoYXkgbXVlcnRlcyB0b2RvcyBsb3MgYcOxb3MgDQphbnlvcyA8LSBkYXRhLmZyYW1lKA0KICAib3JkZW4iID0gMTo3MSwNCiAgImHDsW8iID0gYygxOTUwOjIwMjApKQ0KDQojc3VtYXRvcmlvIGRlIGxhcyBtdWVydGVzIHBvciBhw7FvDQptdWVydGVzX2FueW8gPC0gbXVlcnRlc2YxICU+JSBncm91cF9ieShmZWNoYV9tdWVydGUpICU+JSBtdXRhdGUobXVlcnRlc3hhbnlvID0gc3VtKG4oKSkpICU+JSBkaXN0aW5jdChmZWNoYV9tdWVydGUsIG11ZXJ0ZXN4YW55bykgDQoNCiNmdXNpb25vIGxvcyAyIGRmcyBwYXJhIHF1ZSB0ZW5nYSBlbiBjdWVudGEgbG9zIGHDsW9zIGRvbmRlIG5vIGhheSBtdWVydGVzDQptdWVydGVzZjFfZmluYWwgPC0gZnVsbF9qb2luKG11ZXJ0ZXNfYW55bywgYW55b3MsIGMoImZlY2hhX211ZXJ0ZSIgPSAiYcOxbyIpKSAlPiUgc2VsZWN0KGZlY2hhX211ZXJ0ZSxtdWVydGVzeGFueW8pICU+JSBhcnJhbmdlKGZlY2hhX211ZXJ0ZSkNCg0KI2NvbnZpZXJ0byBsb3MgTi9BIGVuIDAsIGVzIGRlY2lyLCBjdWFuZG8gbm8gaGF5IG9ic2VydmFjaW9uZXMsIGhhIGhhYmlkbyAwIG11ZXJ0ZXMNCm11ZXJ0ZXNmMV9maW5hbFtpcy5uYShtdWVydGVzZjFfZmluYWwpXSA8LSAwDQoNCiNncmFmaWNvIGRlIGxhcyBtdWVydGVzIHBvciBjYWRhIGHDsW8gKyBsYSB0ZW5kZW5jaWEgbmVnYXRpdmEgZWEgbG8gbGFyZ28gZGUgbGEgaGlzdG9yaWENCmdnX211ZXJ0ZXMgPC0gZ2dwbG90KG11ZXJ0ZXNmMV9maW5hbCwgYWVzKHggPSBmZWNoYV9tdWVydGUsIHkgPSBtdWVydGVzeGFueW8gKSkgKyAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAid2hpdGUiKSArIGdlb21fc21vb3RoKGNvbG91ciA9ICJjeWFuIiwgc2UgPSBGQUxTRSkgKyBsYWJzKHggPSAiQcOxbyIgLCB5ID0gIk7Dum1lcm8gZGUgbXVlcnRlcyIpICArIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAid2hpdGUiKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyYXkxMyIpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyYXkxMyIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIndoaXRlIiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIndoaXRlIiksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXkxMiIsDQogICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIpLCBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTMiKSkgK2xhYnMoY29sb3VyID0gIndoaXRlIikgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJncmF5MzgiLA0KICAgIGxpbmV0eXBlID0gImRvdHRlZCIpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9IE5BKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNSwNCiAgICAgICAgaGp1c3QgPSAwLjUpKSArbGFicyh0aXRsZSA9ICJBY2NpZGVudGVzIG1vcnRhbGVzIHBvciBhw7FvIikgKyBnZW9tX3RleHQoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IDIwMDQuMTA1MjI2NDI4NzUsIHkgPSAwLjIzNzQ1MDUxNjk0MjI0MSwgDQogICAgbGFiZWwgPSAiVGVuZGVuY2lhIG5lZ2F0aXZhIiksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSB5LCANCiAgICBsYWJlbCA9IGxhYmVsKSwgY29sb3VyID0gImN5YW4iLCBpbmhlcml0LmFlcyA9IEZBTFNFLCBzaXplID0gMykNCg0KZ2dwbG90bHkoZ2dfbXVlcnRlcykNCg0KDQoNCmBgYA0KDQojIFsxLiBJbnRyb2R1Y2Npw7NuXXsudmVyZGVjaXRvfQ0KDQpUZW5lbW9zIHBlbnNhZG8gZWxhYm9yYXIgZWwgdHJhYmFqbyBlbiBlcXVpcG8gc29icmUgRm9ybXVsYSAxLCB1bmEgY29tcGV0aWNpw7NuIGRlIGxhIHF1ZSBzb21vcyBtdXkgYWZpY2lvbmFkb3MsIGVudHJlIG90cmFzIGNvc2FzIHBvciBsYSBpbXBvcnRhbmNpYSBxdWUgdGllbmVuIGxvcyBkYXRvcyBhIGxhIGhvcmEgZGUgZm9ybWFsaXphciBsYXMgZXN0cmF0ZWdpYXMgZW4gbGEgY29tcGV0aWNpw7NuLiANCg0KDQojIFJlbWFuZG8gYSBjb250cmFjb3JyaWVudGUNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCiNtYXMgcG9zaWNpb25lcyByZW1vbnRhZGFzIGVuIHVuYSBjYXJyZXJhIGdyYW4gcHJlbWlvDQoNCnRpZW1wb3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2xhcF90aW1lcy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQpjaXJjdWl0b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NpcmN1aXRzLmNzdiIpDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQoNCnJlc3VsdGFkb3NbLCBjKDYsOSldIDwtIHNhcHBseShyZXN1bHRhZG9zWywgYyg2LDkpXSwgYXMubnVtZXJpYykgI3RyYW5zZm9ybW8gdmFyaWFibGVzIGdyaWQgeSBwb3NpdGlvbk9yZGVyIGVuIG51bWVyaWNvDQpzdHIocmVzdWx0YWRvcykgIyBwYXJhIGNvbXByb2JhcmxvDQoNCg0KI21heW9yZXMgcmVtb250YWRhcyBkZSBsYSBoaXN0b3JpYSwgc2UgcmVzdGEgcG9zaWNpb24gZGUgc2FsaWRhIC0gcG9zaWNpb24gZmluYWwNCnB1ZXN0b3NfcmVtb250YWRvcyA8LSByZXN1bHRhZG9zICU+JSBtdXRhdGUocmVtb250YWRvcyA9IGdyaWQgLSBwb3NpdGlvbk9yZGVyKSAlPiUgc2VsZWN0KHJhY2VJZCwgZHJpdmVySWQsIGdyaWQsIHBvc2l0aW9uT3JkZXIsIHJlbW9udGFkb3MpIA0KDQoNCiNkZSB0b2RhIGxhIGhpc3RvcmlhDQpjaXJjdWl0b3NfZ3AgPC0gZnVsbF9qb2luKGNhcnJlcmFzLCBjaXJjdWl0b3MsIGMoImNpcmN1aXRJZCIgPSAiY2lyY3VpdElkIikpICU+JSBzZWxlY3QoY2lyY3VpdElkLCBuYW1lLnksIHJhY2VJZCwgeWVhcikNCg0KcHRvc19yZW1vbnRfY2FycmVyYSA8LSBpbm5lcl9qb2luKHB1ZXN0b3NfcmVtb250YWRvcywgY2lyY3VpdG9zX2dwKQ0KDQpwdWVzdG9zX3JlbW9udF9waWxvdG8gPC0gZnVsbF9qb2luKHBpbG90b3MsIHB0b3NfcmVtb250X2NhcnJlcmEsIGMoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2xpY2VfbWF4KHJlbW9udGFkb3MsIG49MTApICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZixuYW1lLnkseWVhciwgcmFjZUlkLCBncmlkLCBwb3NpdGlvbk9yZGVyLCByZW1vbnRhZG9zKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KIyBkZSBsYSBoaXNvdG9yaWEgcmVjaWVudGUNCmNpcmN1aXRvc19ncF9yZWNpZW50IDwtIGZ1bGxfam9pbihjYXJyZXJhcywgY2lyY3VpdG9zLCBjKCJjaXJjdWl0SWQiID0gImNpcmN1aXRJZCIpKSAlPiUgc2VsZWN0KGNpcmN1aXRJZCwgbmFtZS55LCByYWNlSWQsIHllYXIpICU+JSBmaWx0ZXIoeWVhciA+PSAxOTk1KQ0KDQpwdG9zX3JlbW9udF9jYXJyZXJhX3JlY2llbnQgPC0gaW5uZXJfam9pbihwdWVzdG9zX3JlbW9udGFkb3MsIGNpcmN1aXRvc19ncF9yZWNpZW50KQ0KDQpwdWVzdG9zX3JlbW9udF9waWxvdG9fcmVjaWVudCA8LSBmdWxsX2pvaW4ocGlsb3RvcywgcHRvc19yZW1vbnRfY2FycmVyYV9yZWNpZW50LCBjKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNsaWNlX21heChyZW1vbnRhZG9zLCBuPTEwKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hbWUueSwgeWVhcixyYWNlSWQsIGdyaWQsIHBvc2l0aW9uT3JkZXIsIHJlbW9udGFkb3MpICU+JSBzbGljZSgxOjQsNjo4LDEwKSAlPiUgYXJyYW5nZShkZXNjKHJlbW9udGFkb3MpKQ0KDQpnZ3JlbW9udGFkb3MgPC0gZ2dwbG90KHB1ZXN0b3NfcmVtb250X3BpbG90b19yZWNpZW50LCBhZXMoeCA9IHJlb3JkZXIoZHJpdmVyUmVmLCByZW1vbnRhZG9zKSwgcmVtb250YWRvcykpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgY29vcmRfZmxpcCgpICsgbGFicyh4ID0gIlBpbG90b3MiLCB5ID0gIk7CuiBkZSBwdWVzdG9zIHJlbW9udGFkb3MiICkNCmdncmVtb250YWRvcw0KDQpvYmpldG9zX25vX2JvcnJhciA8LSBjKCJ2aWN0b3JpYXNfY29uX25vbWJyZSIsICJuX2NhcnJlcmFzX25vbSIsICJtYXNfdmljdG9yaWFzIikNCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKQ0KYGBgDQoNCg0KIyBBTE9OU08gdnMgSEFNSUxUT04NCg0KYGBge3IsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQ0KDQojc2UgbmVjZXNpdGEgdGVuZXIgY2FyZ2FkbyAibl9jYXJyZXJhc19ub20iLCAidmljdG9yaWFzX2Nvbl9ub21icmUiDQojMTIzNA0KZm90b3NfQUxPX3ZzX0hBTSA8LSBjKCIuL2ltYWdlbmVzL3BpbG90b3MvYWxvbnNvLnBuZyIsICIuL2ltYWdlbmVzL3BpbG90b3MvaGFtaWx0b24ucG5nIikNCmZvdG9zX2VzcF9pbmcgPC0gYygiLi9pbWFnZW5lcy9wYWlzZXMvZXNwYW55YS5wbmciLCAiLi9pbWFnZW5lcy9wYWlzZXMvdWsucG5nIikNCm5fY2FycmVyYXNfYWxvX2hhbSA8LSBuX2NhcnJlcmFzX25vbSAlPiUgZmlsdGVyKGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJoYW1pbHRvbiIpKQ0KDQpuX3ZpY3Rvcmlhc19hbG9faGFtIDwtIHZpY3Rvcmlhc19jb25fbm9tYnJlICU+JSBmaWx0ZXIoZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgImhhbWlsdG9uIikpDQoNCmFsb192c19oYW0gPC0gZnVsbF9qb2luKG5fY2FycmVyYXNfYWxvX2hhbSwgbl92aWN0b3JpYXNfYWxvX2hhbSwgYygiZHJpdmVyUmVmIj0gImRyaXZlclJlZiIpKSAlPiUgc2VsZWN0KCBkcml2ZXJSZWYsIG51bWVyb19jYXJyZXJhcywgbl92aWN0b3JpYXMpICU+JSBhZGRfY29sdW1uKGZvdG9zX2VzcF9pbmcsIGZvdG9zX0FMT192c19IQU0pIA0KDQpsaWJyYXJ5KGd0KQ0KYWxvX3ZzX2hhbV90YWJsYSA8LSBhbG9fdnNfaGFtICU+JSBndCgpICU+JSB0ZXh0X3RyYW5zZm9ybSggbG9jYXRpb25zID0gY2VsbHNfYm9keShjb2x1bW5zID0gYyhmb3Rvc19lc3BfaW5nKSksIGZuID0gZnVuY3Rpb24oeCkge2d0Ojpsb2NhbF9pbWFnZSh4LCBoZWlnaHQgPSA1MCl9KSAlPiUgdGV4dF90cmFuc2Zvcm0oIGxvY2F0aW9ucyA9IGNlbGxzX2JvZHkoY29sdW1ucyA9IGMoZm90b3NfQUxPX3ZzX0hBTSkpLCBmbiA9IGZ1bmN0aW9uKHgpIHtndDo6bG9jYWxfaW1hZ2UoeCwgaGVpZ2h0ID0gMTAwKX0pICU+JSB0YWJfaGVhZGVyKHRpdGxlID0gbWQoIioqQWxvbnNvIHZzIEhhbWlsdG9uKioiKSwgc3VidGl0bGUgPSBtZCgiQ29tcGFyYWNpw7NuIikpICU+JSAgIGNvbHNfbGFiZWwoDQogICAgZHJpdmVyUmVmID0gaHRtbCgiIiksDQogICAgbnVtZXJvX2NhcnJlcmFzID0gaHRtbCgiTsK6IGNhcnJlcmFzIiksDQogICAgbl92aWN0b3JpYXMgPSBodG1sKCJOwrogdmljdG9yaWFzIiksDQogICAgZm90b3NfZXNwX2luZyA9IGh0bWwoIlBhw61zIiksDQogICAgZm90b3NfQUxPX3ZzX0hBTSA9IGh0bWwoIiIpKSAlPiUgIA0KICB0YWJfb3B0aW9ucyh0YWJsZS5iYWNrZ3JvdW5kLmNvbG9yID0gImdyYXkxMyIsICAgdGFibGUuZm9udC5jb2xvci5saWdodCA9ICJjeWFuIikgJT4lIA0KICBjb2xzX2FsaWduKGFsaWduID0gImNlbnRlciIsDQogIGNvbHVtbnMgPSBldmVyeXRoaW5nKCkpDQoNCmFsb192c19oYW1fdGFibGENCmBgYA0KDQoNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQojYXVkaWVuY2lhcw0KDQoNCmF1ZGllbmNpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2F1ZGllbmNpYXNGMS5jc3YiKQ0KDQoNCmdnX2F1ZGllbmNpYXMgPC0gZ2dwbG90KGF1ZGllbmNpYXMsIGFlcyh4PXllYXIsIHk9IG51bWVyb19lc3BlY3RhZG9yZXMpKSArDQogIGdlb21fc2VnbWVudCggYWVzKHg9eWVhciwgeGVuZCA9IHllYXIsIHk9MCwgeWVuZD0gbnVtZXJvX2VzcGVjdGFkb3JlcyAsIHNpemUgPSAiMSIpKSArDQogIGdlb21fcG9pbnQoIHNpemU9NSwgY29sb3I9ImJsdWUiLCBmaWxsPWFscGhhKCJjeWFuIiwgOCksIGFscGhhPTAuNywgc2hhcGU9MjEsIHN0cm9rZT0yKSArICANCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IHNlcSgyMDA0LCAyMDIwLCAxKSwNCiAgICBsaW1pdHMgPSBjKDIwMDMsIDIwMjEpKSArIGxhYnMoeCA9ICJBw7FvIiwgeSA9ICJOdW1lcm8gZGUgZXNwZWN0YWRvcmVzIiApICArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTMiKSwNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTMiKSkgKyB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gIndoaXRlIiksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JheTIwIiksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JheTIwIiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgaGp1c3QgPSAwLjUpKSArbGFicyh0aXRsZSA9ICJFdm9sdWNpw7NuIGRlIGxhIGF1ZGllbmNpYSIsDQogICAgY29sb3VyID0gIndoaXRlIikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNCkpICMrIHRyYW5zaXRpb25fcmV2ZWFsKG51bWVyb19lc3BlY3RhZG9yZXMpDQoNCmdncGxvdGx5KGdnX2F1ZGllbmNpYXMpDQoNCg0KYGBgDQoNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC5leHRyYT0nYW5nbGU9OTAnLCBlY2hvID0gVFJVRX0NCiNwcmVzdXB1ZXN0b3MNCg0KcHJlc3VwdWVzdG9zIDwtIHJlYWRfZXhjZWwoImRhdG9zL3ByZXN1cHVlc3Rvcy54bHN4IikNCg0KZ2dfcHJlc3VwIDwtIGdncGxvdChwcmVzdXB1ZXN0b3MsIGFlcyh5ZWFyLCBQcmVzdXB1ZXN0bywgY29sb3IgPSBFc2N1ZGVyaWEpKSArIA0KICBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArIA0KICBsYWJzKHggPSAiQcOxbyIsIHkgPSAiUHJlc3VwdWVzdG8gZW4g4oKsIiApICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoDQogICAgYnJlYWtzID0gc2VxKDIwMTUsIDIwMjMsIDEpLA0KICAgIGxpbWl0cyA9IGMoMjAxNCwgMjAyNCkpICsgDQogIHNjYWxlX3lfY29udGludW91cyggYnJlYWtzID0gc2VxKDAsIDcwMDAwMDAwMCwgMTAwMDAwMDAwKSwNCiAgICBsaW1pdHMgPSBjKDAsIDYwMDAwMDAwMCkpICArIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gIndoaXRlIiksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAid2hpdGUiLA0KICAgICAgICBsaW5ldHlwZSA9ICJibGFuayIpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgIGxpbmV0eXBlID0gImJsYW5rIiksIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LA0KICAgICAgICBmYWNlID0gImJvbGQiLCBjb2xvdXIgPSAiY3lhbiIsIHZqdXN0ID0gMC43NSksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LA0KICAgICAgICBmYWNlID0gImJvbGQiLCBjb2xvdXIgPSAiY3lhbiIsIGhqdXN0ID0gMC41LA0KICAgICAgICB2anVzdCA9IDAuNzUpLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLA0KICAgICAgICBjb2xvdXIgPSAiY3lhbiIpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLA0KICAgICAgICBmYWNlID0gImJvbGQiLCBjb2xvdXIgPSAiY3lhbiIpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTIiLA0KICAgICAgICBjb2xvdXIgPSAid2hpdGUiKSwgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTEyIiksDQogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXkxMiIpLA0KICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTEyIikpICtsYWJzKHRpdGxlID0gIlBSRVNVUFVFU1RPIERFIENBREEgRVFVSVBPIFBPUiBURU1QT1JBREEiKSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gTkEpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gTkEpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTIiLA0KICAgICAgICBjb2xvdXIgPSBOQSksIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXkxMiIsDQogICAgICAgIGNvbG91ciA9IE5BKSkgKyB0aGVtZShsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTEyIiksDQogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTIiKSkNCiANCmdncGxvdGx5KGdnX3ByZXN1cCkgI3BhcmEgcXVlIHNlYSBpbnRlcmFjdGl2bw0KYGBgDQojIyAqKlRIRSBQTEFOKioNCg0KYGBge3IsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkobWVtZSkNCiNteV9mb3RvIDwtICJodHRwczovL2UwMC1lbG11bmRvLnVlY2RuLmVzL2Fzc2V0cy9tdWx0aW1lZGlhL2ltYWdlbmVzLzIwMTcvMDUvMTIvMTQ5NDU4OTkzNDYzMDYuanBnIg0KI21lbWUobXlfZm90bywgIk1VWSBUUkFOUVVJTE9TIiwgIlNFIFZJRU5FIFRSQUJBSkFaTyIsIHNpemUgPSAyICwgY29sb3IgPSAiYmx1ZSIsIHZqdXN0ID0gMS4wNSkNCg0KYGBgDQoNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQoNCnBpbG90b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2RyaXZlcnMuY3N2IikNCg0KbmFjaW9uYWxpZGFkIDwtIHBpbG90b3MgJT4lIGdyb3VwX2J5KG5hdGlvbmFsaXR5KSAlPiUgDQogIG11dGF0ZShudW1lcm9fY29tcGF0cmlvdGFzID0gc3VtKG4oKSkpICU+JSANCiAgZGlzdGluY3QobnVtZXJvX2NvbXBhdHJpb3RhcykgJT4lIGFycmFuZ2UoZGVzYyhudW1lcm9fY29tcGF0cmlvdGFzKSkgJT4lIA0KICBtdXRhdGUoY291bnRyeSA9IGNhc2Vfd2hlbihuYXRpb25hbGl0eSA9PSAiQnJpdGlzaCIgfiAiVW5pdGVkIEtpbmdkb20iLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJBbWVyaWNhbiIgfiAiVW5pdGVkIFN0YXRlcyIsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkl0YWxpYW4iIH4gJ0l0YWx5JywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiRnJlbmNoIiB+ICdGcmFuY2UnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJHZXJtYW4iIH4gJ0dlcm1hbnknLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJCcmF6aWxpYW4iIH4gJ0JyYXppbCcsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkFyZ2VudGluZSIgfiAnQXJnZW50aW5hJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiU3dpc3MiIH4gJ1N3aXR6ZXJsYW5kJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiQmVsZ2lhbiIgfiAnQmVsZ2l1bScsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIlNvdXRoIEFmcmljYW4iIH4gJ1NvdXRoIEFmcmljYScsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkphcGFuZXNlIiB+ICdKYXBhbicsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkF1c3RyYWxpYW4iIH4gJ0F1c3RyYWxpYScsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkR1dGNoIiB+ICdOZXRoZXJsYW5kcycsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIlNwYW5pc2giIH4gJ1NwYWluJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiQXVzdHJpYW4iIH4gJ0F1c3RyaWEnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJDYW5hZGlhbiIgfiAnQ2FuYWRhJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiU3dlZGlzaCIgfiAnU3dlZGVuJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiRmlubmlzaCIgfiAnRmlubGFuZCcsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIk5ldyBaZWFsYW5kZXIiIH4gJ05ldyBaZWFsYW5kJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiTWV4aWNhbiIgfiAnTWV4aWNvJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiSXJpc2giIH4gJ0lyZWxhbmQnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJEYW5pc2giIH4gJ0Rlbm1hcmsnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJQb3J0dWd1ZXNlIiB+ICdQb3J0dWdhbCcsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIk1vbmVnYXNxdWUiIH4gJ0ZyYW5jZScsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIlJob2Rlc2lhbiIgfiAnWmltYmFid2UnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJVcnVndWF5YW4iIH4gJ1VydWd1YXknLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJSdXNzaWFuIiB+ICdSdXNzaWEnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJDb2xvbWJpYW4iIH4gJ0NvbG9tYmlhJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiVmVuZXp1ZWxhbiIgfiAnVmVuZXp1ZWxhJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiRWFzdCBHZXJtYW4iIH4gJ0dlcm1hbicsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkluZGlhbiIgfiAnSW5kaWEnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJUaGFpIiB+ICdUaGFpbGFuZCcsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIlBvbGlzaCIgfiAnUG9sYW5kJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiSHVuZ2FyaWFuIiB+ICdIdW5nYXJ5JywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiQ3plY2giIH4gJ0N6ZWNoIFJlcC4nLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJNYWxheXNpYW4iIH4gJ01hbGF5c2lhJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiQ2hpbGVhbiIgfiAnQ2hpbGUnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJMaWVjaHRlbnN0ZWluZXIiIH4gJ1N3aXR6ZXJsYW5kJywNCiAgICAgICAgICBuYXRpb25hbGl0eSA9PSAiQW1lcmljYW4tSXRhbGlhbiIgfiAnVW5pdGVkIFN0YXRlcycsDQogICAgICAgICAgbmF0aW9uYWxpdHkgPT0gIkFyZ2VudGluZS1JdGFsaWFuIiB+ICdBcmdlbnRpbmEnLA0KICAgICAgICAgIG5hdGlvbmFsaXR5ID09ICJJbmRvbmVzaWFuIiB+ICdJbmRvbmVzaWEnKSkNCg0KDQpsaWJyYXJ5KHRtYXApDQpkYXRhKFdvcmxkKQ0Kd29ybGQgPC0gV29ybGQ7IHJtKFdvcmxkKQ0KYGBgDQojIDIuIERhdG9zDQoNCkhlbW9zIGVuY29udHJhZG8gZW4gW2thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS8pIGJhc3RhbnRlcyBjb25qdW50b3MgZGUgZGF0b3MgY29uIGxvcyBxdWUgcG9kZXIgdHJhYmFqYXIsIHBlcm8gZXNwZWNpYWxtZW50ZSBbZXN0ZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9yb2hhbnJhby9mb3JtdWxhLTEtd29ybGQtY2hhbXBpb25zaGlwLTE5NTAtMjAyMCksIHF1ZSBwb3NlZSBncmFuIHZhcmllZGFkIGRlIGRhdG9zIGVuIGxvIHJlZmVyZW50ZSBhIHBpbG90b3MsIHJlc3VsdGFkb3MsIGNpcmN1aXRvcywgdGllbXBvcywgZXRjLi4uIENvbnNpZGVyYW1vcyBxdWUgcGFyYSBlbXBlemFyIGEgdHJhYmFqYXIgc2Vyw6Egc3VmaWNpZW50ZSwgeSBlbiBmdW5jacOzbiBkZSBjb21vIHZheWFtb3MgZGlyaWdpZW5kbyBlbCB0cmFiYWpvLCBidXNjYXJlbW9zIGRpZmVyZW50ZXMgY29uanVudG8gZGUgZGF0b3MgY29uIGxvcyBxdWUgYXBveWFybm9zLg0KDQojIDMuIFRyYWJham9zIGVuIGxvcyBxdWUgbm9zIHZhbW9zIGEgYmFzYXINCg0KQ29uIGxvcyBkYXRvcyBxdWUgaGVtb3MgZW5jb250cmFkbywgZXhpc3RlbiB1bmEgc2VyaWUgZGUgY8OzZGlnb3MgcXVlIHlhIHRyYWJhamFuIGNvbiBlc3RvcyBkYXRvcywgZXNwZWNpYWxtZW50ZSBbZXN0ZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9la3JlbWJheWFyL2Zvcm11bGEtMS03MHRoLWFubml2ZXJzYXJ5KSwgcXVlIGhhIGNvbnNlZ3VpZG8gcmVhbGl6YXIgYW7DoWxpc2lzIGNvbiBlc3RlIGNvbmp1bnRvIGRlIGRhdG9zIHkgdmFyaWFzIGlsdXN0cmFjaW9uZXMgbXV5IGxsYW1hdGl2YXMsIHBvciBsbyBxdWUgcG9kcmVtb3MgdG9tYXJsbyBjb21vIHJlZmVyZW5jaWEgZHVyYW50ZSBlbCBpbmljaW8gZGVsIHRyYWJham8NCg0KPiAiTmFkaWUgZXMgbcOhcyByw6FwaWRvIHF1ZSBlbCBuYW5vIg0K